Thinger.io Client Library for ARM mbed platform. This is a generic library that provides a base class that can be used to other develop hardware specific libraries.

Fork of ThingerClient by Alvaro Luis Bustamante

Files at this revision

API Documentation at this revision

Comitter:
alvarolb
Date:
Thu Dec 24 13:18:08 2015 +0000
Child:
1:d54f92accbc3
Commit message:
Initial Commit

Changed in this revision

ThingerClient.h Show annotated file Show diff for this revision Revisions of this file
thinger/pson.h Show annotated file Show diff for this revision Revisions of this file
thinger/thinger.h Show annotated file Show diff for this revision Revisions of this file
thinger/thinger_decoder.hpp Show annotated file Show diff for this revision Revisions of this file
thinger/thinger_encoder.hpp Show annotated file Show diff for this revision Revisions of this file
thinger/thinger_io.hpp Show annotated file Show diff for this revision Revisions of this file
thinger/thinger_map.hpp Show annotated file Show diff for this revision Revisions of this file
thinger/thinger_message.hpp Show annotated file Show diff for this revision Revisions of this file
thinger/thinger_resource.hpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThingerClient.h	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,220 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef THINGER_CLIENT_H
+#define THINGER_CLIENT_H
+
+#include "thinger/thinger.h"
+
+using namespace protoson;
+
+dynamic_memory_allocator alloc;
+//circular_memory_allocator<512> alloc;
+memory_allocator& protoson::pool = alloc;
+
+#define THINGER_SERVER "iot.thinger.io"
+#define THINGER_PORT 25200
+#define RECONNECTION_TIMEOUT 5 // seconds
+
+class ThingerClient : public thinger::thinger {
+public:
+    ThingerClient(const char* user, const char* device, const char* device_credential) :
+            username_(user), device_id_(device), device_password_(device_credential),
+            temp_data_(NULL), out_size_(0)
+    {}
+
+    virtual ~ThingerClient()
+    {}
+
+protected:
+
+    virtual bool    socket_start(const char* host, int port) = 0;
+    virtual bool    socket_stop() = 0;
+    virtual bool    socket_connected() = 0;
+    virtual size_t  socket_read(char* buffer, size_t size) = 0;
+    virtual size_t  socket_write(char* buffer, size_t size) = 0;
+    virtual size_t  socket_available() = 0;
+    virtual bool    connect_network() = 0;
+    virtual bool    network_connected() = 0;
+
+    virtual bool read(char *buffer, size_t size)
+    {
+        size_t total_read = 0;
+        while(total_read<size){
+            int read = socket_read(buffer, size-total_read);
+            if(read<0) return false;
+            total_read += read;
+        }
+        return total_read == size;
+    }
+
+    // TODO Allow removing this Nagle's algorithm implementation if the underlying device already implements it
+    virtual bool write(const char *buffer, size_t size, bool flush = false){
+        if(size>0){
+            temp_data_ = (char*) realloc(temp_data_, out_size_ + size);
+            memcpy(&temp_data_[out_size_], buffer, size);
+            out_size_ += size;
+        }
+        if(flush && out_size_>0){
+            size_t written = socket_write(temp_data_, out_size_);
+            bool success = written == out_size_;
+            free(temp_data_);
+            temp_data_ = NULL;
+            out_size_ = 0;
+            return success;
+        }
+        return true;
+    }
+
+    virtual void disconnected(){
+        thinger_state_listener(SOCKET_TIMEOUT);
+        socket_stop();
+        thinger_state_listener(SOCKET_DISCONNECTED);
+    }
+    
+    enum THINGER_STATE{
+        NETWORK_CONNECTING,
+        NETWORK_CONNECTED,
+        NETWORK_CONNECT_ERROR,
+        SOCKET_CONNECTING,
+        SOCKET_CONNECTED,
+        SOCKET_CONNECTION_ERROR,
+        SOCKET_DISCONNECTED,
+        SOCKET_TIMEOUT,
+        THINGER_AUTHENTICATING,
+        THINGER_AUTHENTICATED,
+        THINGER_AUTH_FAILED
+    };
+
+    virtual void thinger_state_listener(THINGER_STATE state){
+        #ifdef _DEBUG_
+        switch(state){
+            case NETWORK_CONNECTING:
+                printf("[NETWORK] Starting connection...\n");
+                break;
+            case NETWORK_CONNECTED:
+                printf("[NETWORK] Connected!\n");
+                break;
+            case NETWORK_CONNECT_ERROR:
+                printf("[NETWORK] Cannot connect\n!");
+                break;
+            case SOCKET_CONNECTING:
+                printf("[_SOCKET] Connecting to %s:%d...\n", THINGER_SERVER, THINGER_PORT);
+                break;
+            case SOCKET_CONNECTED:
+                printf("[_SOCKET] Connected!\n");
+                break;
+            case SOCKET_CONNECTION_ERROR:
+                printf("[_SOCKET] Error while connecting!\n");
+                break;
+            case SOCKET_DISCONNECTED:
+                printf("[_SOCKET] Is now closed!\n");
+                break;
+            case SOCKET_TIMEOUT:
+                printf("[_SOCKET] Timeout!\n");
+                break;
+            case THINGER_AUTHENTICATING:
+                printf("[THINGER] Authenticating. User: %s Device: %s\n", username_, device_id_);
+                break;
+            case THINGER_AUTHENTICATED:
+                printf("[THINGER] Authenticated!\n");
+                break;
+            case THINGER_AUTH_FAILED:
+                printf("[THINGER] Auth Failed! Check username, device id, or device credentials.\n");
+                break;
+        }
+        #endif
+    }
+
+    bool handle_connection()
+    {
+        bool network = network_connected();
+
+        if(!network){
+            thinger_state_listener(NETWORK_CONNECTING);
+            network = connect_network();
+            if(!network){
+                thinger_state_listener(NETWORK_CONNECT_ERROR);
+                return false;
+            }
+            thinger_state_listener(NETWORK_CONNECTED);
+        }
+
+        bool client = socket_connected();
+        if(!client){
+            client = connect_client();
+            if(!client){
+                return false;
+            }
+        }
+        return network && client;
+    }
+
+    bool connect_client(){
+        bool connected = false;
+        socket_stop(); // cleanup previous socket
+        thinger_state_listener(SOCKET_CONNECTING);
+        if (socket_start(THINGER_SERVER, THINGER_PORT)) {
+            thinger_state_listener(SOCKET_CONNECTED);
+            thinger_state_listener(THINGER_AUTHENTICATING);
+            connected = thinger::thinger::connect(username_, device_id_, device_password_);
+            if(!connected){
+                thinger_state_listener(THINGER_AUTH_FAILED);
+                socket_stop();
+                thinger_state_listener(SOCKET_DISCONNECTED);
+            }
+            else{
+                thinger_state_listener(THINGER_AUTHENTICATED);
+            }
+        }
+        else{
+            thinger_state_listener(SOCKET_CONNECTION_ERROR);
+        }
+        return connected;
+    }
+
+public:
+
+    void handle(){
+        if(handle_connection()){
+            #ifdef _DEBUG_
+            int available = socket_available();
+            if(available>0){
+                printf("[THINGER] Available bytes: %d\n", available);
+            }
+            #endif
+            thinger::thinger::handle(us_ticker_read()/1000, available>0);
+        }else{
+            wait(RECONNECTION_TIMEOUT); // get some delay for a connection retry
+        }
+    }
+
+private:
+    const char* username_;
+    const char* device_id_;
+    const char* device_password_;
+    char * temp_data_;
+    size_t out_size_;
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/pson.h	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,902 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef PSON_HPP
+#define PSON_HPP
+
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+namespace protoson {
+
+    class memory_allocator{
+    public:
+        virtual void *allocate(size_t size) = 0;
+        virtual void deallocate(void *) = 0;
+    };
+
+    template<size_t buffer_size>
+    class circular_memory_allocator : public memory_allocator{
+    private:
+        uint8_t buffer_[buffer_size];
+        size_t index_;
+    public:
+        circular_memory_allocator() : index_(0) {
+        }
+
+        virtual void *allocate(size_t size) {
+            if (index_ + size > buffer_size) {
+                index_ = 0;
+            }
+            void *position = &buffer_[index_];
+            index_ += size;
+            return position;
+        }
+
+        virtual void deallocate(void *) {
+
+        }
+    };
+
+    class dynamic_memory_allocator : public memory_allocator{
+    public:
+        virtual void *allocate(size_t size) {
+            return malloc(size);
+        }
+
+        virtual void deallocate(void *ptr) {
+            free(ptr);
+        }
+    };
+
+    extern memory_allocator& pool;
+}
+
+inline void* operator new(size_t sz, protoson::memory_allocator& a)
+{
+    return a.allocate(sz);
+}
+
+inline void operator delete(void* p, protoson::memory_allocator& a)
+{
+    a.deallocate(p);
+}
+
+template<class T>
+void destroy(T* p, protoson::memory_allocator& a)
+{
+    if (p) {
+        p->~T();
+        a.deallocate(p);
+    }
+}
+
+namespace protoson {
+
+    enum pb_wire_type{
+        varint = 0,
+        fixed_64 = 1,
+        length_delimited = 2,
+        fixed_32 = 5,
+        pson_type = 6
+    };
+
+    template<class T>
+    class pson_container {
+
+        struct list_item{
+            T item_;
+            list_item* next_;
+        };
+
+    public:
+
+        class iterator{
+        public:
+            iterator(list_item *current) : current(current) {
+            }
+
+        private:
+            list_item * current;
+
+        public:
+            bool next(){
+                if(current==NULL) return false;
+                current = current->next_;
+                return true;
+            }
+
+            bool has_next(){
+                return current!=NULL && current->next_!=NULL;
+            }
+
+            bool valid(){
+                return current!=NULL;
+            }
+
+            T& item(){
+                return current->item_;
+            }
+        };
+
+    private:
+        list_item* item_;
+
+    public:
+        iterator begin() const{
+            return iterator(item_);
+        }
+
+        pson_container() : item_(NULL) {
+        }
+
+        ~pson_container(){
+            clear();
+        }
+
+        size_t size() const{
+            size_t size = 0;
+            list_item* current = item_;
+            while(current!=NULL){
+                current = current->next_;
+                size++;
+            }
+            return size;
+        }
+
+        T* operator[](size_t index){
+            list_item* current = item_;
+            size_t current_index = 0;
+            while(current!=NULL){
+                if(current_index==index){
+                    return &current->item_;
+                }
+                current = current->next_;
+                current_index++;
+            }
+            return NULL;
+        }
+
+        void clear(){
+            list_item* current = item_;
+            while(current!=NULL){
+                list_item* next = current->next_;
+                destroy(current, pool);
+                current = next;
+            }
+            item_ = NULL;
+        }
+
+        T& create_item(){
+            list_item* new_list_item = new(pool) list_item();
+            if(item_==NULL){
+                item_ = new_list_item;
+            }
+            else{
+                list_item * last = item_;
+                while(last->next_!=NULL) last = last->next_;
+                last->next_ = new_list_item;
+            }
+            return new_list_item->item_;
+        }
+    };
+
+    class pson_object;
+    class pson_array;
+
+    class pson {
+    public:
+        enum field_type {
+            null_field      = 0,
+            varint_field    = 1,
+            svarint_field   = 2,
+            float_field     = 3,
+            double_field    = 4,
+            true_field      = 5,
+            false_field     = 6,
+            zero_field      = 7,
+            one_field       = 8,
+            string_field    = 9,
+            empty_string    = 10,
+            bytes_field     = 11,
+            empty_bytes     = 12,
+            object_field    = 13,
+            array_field     = 14,
+            empty           = 15,
+            // a message tag is encoded in a 128-base varint [1-bit][3-bit wire type][4-bit field]
+            // we have up to 4 bits (0-15) for encoding fields in the first byte
+        };
+
+        bool is_boolean() const{
+            return field_type_ == true_field || field_type_ == false_field;
+        }
+
+        bool is_string() const{
+            return field_type_ == string_field;
+        }
+
+        bool is_bytes() const{
+            return field_type_ == bytes_field;
+        }
+
+        bool is_number() const{
+            return  field_type_ == varint_field     ||
+                    field_type_ == svarint_field    ||
+                    field_type_ == float_field      ||
+                    field_type_ == double_field     ||
+                    field_type_ == zero_field       ||
+                    field_type_ == one_field;
+        }
+
+        bool is_object() const{
+            return field_type_ == object_field;
+        }
+
+        bool is_array() const{
+            return field_type_ == array_field;
+        }
+
+        bool is_null() const{
+            return field_type_ == null_field;
+        }
+
+        bool is_empty() const{
+            return field_type_ == empty;
+        }
+
+        pson() : field_type_(empty), value_(NULL) {
+        }
+
+        template<class T>
+        pson(T value) : field_type_(empty), value_(NULL){
+            *this = value;
+        }
+
+        ~pson(){
+            if(field_type_==object_field){
+                destroy((pson_object *) value_, pool);
+            }else if(field_type_==array_field) {
+                destroy((pson_array *) value_, pool);
+            }else{
+                pool.deallocate(value_);
+            }
+        }
+
+        template<class T>
+        void operator=(T value)
+        {
+            if(value==0){
+                field_type_ = zero_field;
+            }else if(value==1) {
+                field_type_ = one_field;
+            }else{
+                if(value<0){
+                    field_type_ = svarint_field;
+                }else{
+                    field_type_ = varint_field;
+                }
+                uint64_t uint_value = value>0 ? value : -value;
+                value_ = pool.allocate(pson::get_varint_size(uint_value));
+                pb_encode_varint(uint_value);
+            }
+        }
+
+        void operator=(bool value){
+            field_type_ = value ? true_field : false_field;
+        }
+
+        void operator=(float value) {
+            if(value==(int32_t)value){
+                *this = (int32_t) value;
+            }else{
+                field_type_ = float_field;
+                set(value);
+            }
+        }
+
+        void operator=(double value) {
+            if(value==(int64_t)value) {
+                *this = (int64_t) value;
+            }else if(fabs(value-(float)value)<=0.00001){
+                field_type_ = float_field;
+                set((float)value);
+            }else{
+                field_type_ = double_field;
+                set(value);
+            }
+        }
+
+        void operator=(const char *str) {
+            size_t str_size = strlen(str);
+            if(str_size==0){
+                field_type_ = empty_string;
+            }else{
+                field_type_ = string_field;
+                memcpy(allocate(str_size+1), str, str_size+1);
+            }
+        }
+
+        operator const char *() {
+            switch(field_type_){
+                case string_field:
+                    return (const char*) value_;
+                case empty:
+                    field_type_ = empty_string;
+                default:
+                    return "";
+            }
+        }
+
+        void set_bytes(const void* bytes, size_t size) {
+            if(size>0){
+                size_t varint_size = get_varint_size(size);
+                value_ = pool.allocate(varint_size+size);
+                pb_encode_varint(size);
+                memcpy(((uint8_t*)value_)+varint_size, bytes, size);
+                field_type_ = bytes_field;
+            }else{
+                field_type_ = empty_bytes;
+            }
+        }
+
+        bool get_bytes(const void*& bytes, size_t& size){
+            switch(field_type_){
+                case bytes_field:
+                    size = pb_decode_varint();
+                    bytes = (uint8_t*) value_ + get_varint_size(size);
+                    return true;
+                case empty:
+                    field_type_ = empty_bytes;
+                default:
+                    return false;
+            }
+        }
+
+        void* allocate(size_t size){
+            value_ = pool.allocate(size);
+            return value_;
+        }
+
+        operator pson_object &();
+        operator pson_array &();
+        pson & operator[](const char *name);
+
+        operator bool(){
+            switch(field_type_){
+                case zero_field:
+                case false_field:
+                    return false;
+                case one_field:
+                case true_field:
+                    return true;
+                case empty:
+                    field_type_ = false_field;
+                default:
+                    return false;
+            }
+        }
+
+        template<class T>
+        operator T() {
+            switch(field_type_){
+                case zero_field:
+                case false_field:
+                    return 0;
+                case one_field:
+                case true_field:
+                    return 1;
+                case float_field:
+                    return *(float*)value_;
+                case double_field:
+                    return *(double*)value_;
+                case varint_field:
+                    return pb_decode_varint();
+                case svarint_field:
+                    return -pb_decode_varint();
+                case empty:
+                    field_type_ = zero_field;
+                default:
+                    return 0;
+            }
+        }
+
+        void* get_value(){
+            return value_;
+        }
+
+        void set_value(void* value){
+            value_ = value;
+        }
+
+        field_type get_type() const{
+            return field_type_;
+        }
+
+        void set_null(){
+            field_type_ = null_field;
+        }
+
+        void set_type(field_type type){
+            field_type_ = type;
+        }
+
+        uint8_t get_varint_size(uint64_t value) const{
+            uint8_t size = 1;
+            while(value>>=7) size++;
+            return size;
+        }
+
+        void pb_encode_varint(uint64_t value) const
+        {
+            uint8_t count = 0;
+            do
+            {
+                uint8_t byte = (uint8_t)(value & 0x7F);
+                value >>= 7;
+                if(value) byte |= 0x80;
+                ((uint8_t*)value_)[count] = byte;
+                count++;
+            }while(value);
+        }
+
+        uint64_t pb_decode_varint() const
+        {
+            if(value_==NULL) return 0;
+            uint64_t value = 0;
+            uint8_t pos = 0;
+            uint8_t byte = 0;
+            do{
+                byte = ((uint8_t*)value_)[pos];
+                value |= (uint64_t)(byte&0x7F) << pos*7;
+                pos++;
+            }while(byte>=0x80);
+            return value;
+        }
+
+    private:
+        void* value_;
+        field_type field_type_;
+
+        template<class T>
+        void set(T value) {
+            memcpy(allocate(sizeof(T)), &value, sizeof(T));
+        }
+    };
+
+    class pson_pair{
+    private:
+        char* name_;
+        pson value_;
+    public:
+        pson_pair() : name_(NULL){
+        }
+
+        ~pson_pair(){
+            destroy(name_, pool);
+        }
+
+        void set_name(const char *name) {
+            size_t name_size = strlen(name) + 1;
+            memcpy(allocate_name(name_size), name, name_size);
+        }
+
+        char* allocate_name(size_t size){
+            name_ = (char*)pool.allocate(size);
+            return name_;
+        }
+
+        pson& value(){
+            return value_;
+        }
+
+        char* name() const{
+            return name_;
+        }
+    };
+
+    class pson_object : public pson_container<pson_pair> {
+    public:
+        pson &operator[](const char *name) {
+            for(iterator it=begin(); it.valid(); it.next()){
+                if(strcmp(it.item().name(), name)==0){
+                    return it.item().value();
+                }
+            }
+            pson_pair & pair = create_item();
+            pair.set_name(name);
+            return pair.value();
+        };
+    };
+
+    class pson_array : public pson_container<pson> {
+    public:
+        template<class T>
+        pson_array& add(T item_value){
+            create_item() = item_value;
+            return *this;
+        }
+    };
+
+    inline pson::operator pson_object &() {
+        if (field_type_ != object_field) {
+            value_ = new(pool) pson_object;
+            field_type_ = object_field;
+        }
+        return *((pson_object *)value_);
+    }
+
+    inline pson::operator pson_array &() {
+        if (field_type_ != array_field) {
+            value_ = new(pool) pson_array;
+            field_type_ = array_field;
+        }
+        return *((pson_array *)value_);
+    }
+
+    inline pson &pson::operator[](const char *name) {
+        return ((pson_object &) *this)[name];
+    }
+
+    ////////////////////////////
+    /////// PSON_DECODER ///////
+    ////////////////////////////
+
+    class pson_decoder {
+
+    protected:
+        size_t read_;
+
+        virtual bool read(void* buffer, size_t size){
+            read_+=size;
+            return true;
+        }
+
+    public:
+
+        pson_decoder() : read_(0) {
+
+        }
+
+        void reset(){
+            read_ = 0;
+        }
+
+        size_t bytes_read(){
+            return read_;
+        }
+
+        bool pb_decode_tag(pb_wire_type& wire_type, uint32_t& field_number)
+        {
+            uint32_t temp = pb_decode_varint32();
+            wire_type = (pb_wire_type)(temp & 0x07);
+            field_number = temp >> 3;
+            return true;
+        }
+
+        uint32_t pb_decode_varint32()
+        {
+            uint32_t varint = 0;
+            if(try_pb_decode_varint32(varint)){
+                return varint;
+            }
+            return 0;
+        }
+
+        bool try_pb_decode_varint32(uint32_t& varint){
+            varint = 0;
+            uint8_t byte;
+            uint8_t bit_pos = 0;
+            do{
+                if(!read(&byte, 1) || bit_pos>=32){
+                    return false;
+                }
+                varint |= (uint32_t)(byte&0x7F) << bit_pos;
+                bit_pos += 7;
+            }while(byte>=0x80);
+            return true;
+        }
+
+        uint64_t pb_decode_varint64()
+        {
+            uint64_t varint = 0;
+            uint8_t byte;
+            uint8_t bit_pos = 0;
+            do{
+                if(!read(&byte, 1) || bit_pos>=64){
+                    return varint;
+                }
+                varint |= (uint32_t)(byte&0x7F) << bit_pos;
+                bit_pos += 7;
+            }while(byte>=0x80);
+            return varint;
+        }
+
+        bool pb_skip(size_t size){
+            uint8_t byte;
+            bool success = true;
+            for(size_t i=0; i<size; i++){
+                success &= read(&byte, 1);
+            }
+            return success;
+        }
+
+        bool pb_skip_varint(){
+            uint8_t byte;
+            bool success = true;
+            do{
+                success &= read(&byte, 1);
+            }while(byte>0x80);
+            return success;
+        }
+
+        bool pb_read_string(char *str, size_t size){
+            bool success = read(str, size);
+            str[size]=0;
+            return success;
+        }
+
+        bool pb_read_varint(pson& value)
+        {
+            uint8_t temp[10];
+            uint8_t byte=0;
+            uint8_t bytes_read=0;
+            do{
+                if(!read(&byte, 1)) return false;
+                temp[bytes_read] = byte;
+                bytes_read++;
+            }while(byte>=0x80);
+            memcpy(value.allocate(bytes_read), temp, bytes_read);
+            return true;
+        }
+
+    public:
+
+        void decode(pson_object & object, size_t size){
+            size_t start_read = bytes_read();
+            while(size-(bytes_read()-start_read)>0){
+                decode(object.create_item());
+            }
+        }
+
+        void decode(pson_array & array, size_t size){
+            size_t start_read = bytes_read();
+            while(size-(bytes_read()-start_read)>0){
+                decode(array.create_item());
+            }
+        }
+
+        void decode(pson_pair & pair){
+            uint32_t name_size = pb_decode_varint32();
+            pb_read_string(pair.allocate_name(name_size+1), name_size);
+            decode(pair.value());
+        }
+
+        void decode(pson& value) {
+            uint32_t field_number;
+            pb_wire_type wire_type;
+            pb_decode_tag(wire_type, field_number);
+            value.set_type((pson::field_type)field_number);
+            if(wire_type==length_delimited){
+                uint32_t size = pb_decode_varint32();
+                switch(field_number){
+                    case pson::string_field:
+                        pb_read_string((char*)value.allocate(size + 1), size);
+                        break;
+                    case pson::bytes_field: {
+                        uint8_t varint_size = value.get_varint_size(size);
+                        read((char*)value.allocate(size + varint_size) + varint_size, size);
+                        value.pb_encode_varint(size);
+                    }
+                        break;
+                    case pson::object_field:
+                        value.set_value(new (pool) pson_object);
+                        decode(*(pson_object *)value.get_value(), size);
+                        break;
+                    case pson::array_field:
+                        value.set_value(new (pool) pson_array);
+                        decode(*(pson_array *)value.get_value(), size);
+                        break;
+                    default:
+                        pb_skip(size);
+                        break;
+                }
+            }else {
+                switch (field_number) {
+                    case pson::svarint_field:
+                    case pson::varint_field:
+                        pb_read_varint(value);
+                        break;
+                    case pson::float_field:
+                        read(value.allocate(4), 4);
+                        break;
+                    case pson::double_field:
+                        read(value.allocate(8), 8);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    };
+
+    ////////////////////////////
+    /////// PSON_ENCODER ///////
+    ////////////////////////////
+
+    class pson_encoder {
+
+    protected:
+        size_t written_;
+
+        virtual void write(const void* buffer, size_t size){
+            written_+=size;
+        }
+
+    public:
+
+        pson_encoder() : written_(0) {
+        }
+
+        void reset(){
+            written_ = 0;
+        }
+
+        size_t bytes_written(){
+            return written_;
+        }
+
+        void pb_encode_tag(pb_wire_type wire_type, uint32_t field_number){
+            uint64_t tag = ((uint64_t)field_number << 3) | wire_type;
+            pb_encode_varint(tag);
+        }
+
+        void pb_encode_varint(uint32_t field, uint64_t value)
+        {
+            pb_encode_tag(varint, field);
+            pb_encode_varint(value);
+        }
+
+        uint8_t pb_write_varint(void * buffer)
+        {
+            uint8_t byte=0;
+            uint8_t bytes_written=0;
+            do{
+                byte = *((uint8_t*)buffer + bytes_written);
+                bytes_written++;
+            }while(byte>=0x80);
+            write(buffer, bytes_written);
+            return bytes_written;
+        }
+
+        void pb_encode_varint(uint64_t value)
+        {
+            do
+            {
+                uint8_t byte = (uint8_t)(value & 0x7F);
+                value >>= 7;
+                if(value>0) byte |= 0x80;
+                write(&byte, 1);
+            }while(value>0);
+        }
+
+        void pb_encode_string(const char* str, uint32_t field_number){
+            pb_encode_tag(length_delimited, field_number);
+            pb_encode_string(str);
+        }
+
+        void pb_encode_string(const char* str){
+            size_t string_size = strlen(str);
+            pb_encode_varint(string_size);
+            write(str, string_size);
+        }
+
+        template<class T>
+        void pb_encode_submessage(T& element, uint32_t field_number)
+        {
+            pb_encode_tag(length_delimited, field_number);
+            pson_encoder sink;
+            sink.encode(element);
+            pb_encode_varint(sink.bytes_written());
+            encode(element);
+        }
+
+        void pb_encode_fixed32(void* value){
+            write(value, 4);
+        }
+
+        void pb_encode_fixed64(void* value){
+            write(value, 8);
+        }
+
+        void pb_encode_fixed32(uint32_t field, void*value)
+        {
+            pb_encode_tag(fixed_32, field);
+            pb_encode_fixed32(value);
+        }
+
+        void pb_encode_fixed64(uint32_t field, void*value)
+        {
+            pb_encode_tag(fixed_64, field);
+            pb_encode_fixed64(value);
+        }
+
+    public:
+
+        void encode(pson_object & object){
+            pson_container<pson_pair>::iterator it = object.begin();
+            while(it.valid()){
+                encode(it.item());
+                it.next();
+            }
+        }
+
+        void encode(pson_array & array){
+            pson_container<pson>::iterator it = array.begin();
+            while(it.valid()){
+                encode(it.item());
+                it.next();
+            }
+        }
+
+        void encode(pson_pair & pair){
+            pb_encode_string(pair.name());
+            encode(pair.value());
+        }
+
+        void encode(pson & value) {
+            switch (value.get_type()) {
+                case pson::string_field:
+                    pb_encode_string((const char*)value.get_value(), pson::string_field);
+                    break;
+                case pson::bytes_field:
+                    pb_encode_tag(length_delimited, pson::bytes_field);
+                    write(((const char *) value.get_value()) + pb_write_varint(value.get_value()), value.pb_decode_varint());
+                    break;
+                case pson::svarint_field:
+                case pson::varint_field:
+                    pb_encode_tag(varint, value.get_type());
+                    pb_write_varint(value.get_value());
+                    break;
+                case pson::float_field:
+                    pb_encode_fixed32(pson::float_field, value.get_value());
+                    break;
+                case pson::double_field:
+                    pb_encode_fixed64(pson::double_field, value.get_value());
+                    break;
+                case pson::object_field:
+                    pb_encode_submessage(*(pson_object *) value.get_value(), pson::object_field);
+                    break;
+                case pson::array_field:
+                    pb_encode_submessage(*(pson_array *) value.get_value(), pson::array_field);
+                    break;
+                default:
+                    pb_encode_tag(varint, value.get_type());
+                    break;
+            }
+        }
+    };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/thinger.h	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,262 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef THINGER_H
+#define THINGER_H
+
+#include "pson.h"
+#include "thinger_map.hpp"
+#include "thinger_resource.hpp"
+#include "thinger_message.hpp"
+#include "thinger_encoder.hpp"
+#include "thinger_decoder.hpp"
+#include "thinger_message.hpp"
+#include "thinger_io.hpp"
+
+#define KEEP_ALIVE_MILLIS 60000
+
+namespace thinger{
+
+    using namespace protoson;
+
+    class thinger : public thinger_io{
+    public:
+        thinger() :
+                encoder(*this),
+                decoder(*this),
+                last_keep_alive(0),
+                keep_alive_response(true)
+        {
+        }
+
+        virtual ~thinger(){
+        }
+
+    private:
+        thinger_write_encoder encoder;
+        thinger_read_decoder decoder;
+        unsigned long last_keep_alive;
+        bool keep_alive_response;
+        thinger_map<thinger_resource> resources_;
+
+    protected:
+        /**
+         * Can be override to start reconnection process
+         */
+        virtual void disconnected(){
+            // stop all streaming resources after disconnect
+            if(thinger_resource::get_streaming_counter()>0) {
+                thinger_map<thinger_resource>::entry* current = resources_.begin();
+                while(current!=NULL){
+                    current->value_.disable_streaming();
+                    current = current->next_;
+                }
+            }
+        }
+
+        /**
+         * Stream a given resource
+         */
+        void stream_resource(thinger_resource& resource, thinger_message::signal_flag type){
+            thinger_message message;
+            message.set_stream_id(resource.get_stream_id());
+            message.set_signal_flag(type);
+            resource.fill_api_io(message.get_data());
+            send_message(message);
+        }
+
+    public:
+
+        thinger_resource & operator[](const char* res){
+            return resources_[res];
+        }
+
+        bool connect(const char* username, const char* device_id, const char* credential)
+        {
+            // reset keep alive status for each connection
+            keep_alive_response = true;
+
+            thinger_message message;
+            message.set_signal_flag(thinger_message::AUTH);
+            message.resources().add(username).add(device_id).add(credential);
+            if(!send_message(message)) return false;
+
+            thinger_message response;
+            return read_message(response) && response.get_signal_flag() == thinger_message::REQUEST_OK;
+        }
+
+        bool call_endpoint(const char* endpoint_name){
+            thinger_message message;
+            message.set_signal_flag(thinger_message::CALL_ENDPOINT);
+            message.resources().add(endpoint_name);
+            return send_message(message);
+        }
+
+        bool call_endpoint(const char* endpoint_name, pson& data){
+            thinger_message message;
+            message.set_signal_flag(thinger_message::CALL_ENDPOINT);
+            message.set_data(data);
+            message.resources().add(endpoint_name);
+            return send_message(message);
+        }
+
+        bool call_endpoint(const char* endpoint_name, thinger_resource& resource){
+            thinger_message message;
+            message.set_signal_flag(thinger_message::CALL_ENDPOINT);
+            message.resources().add(endpoint_name);
+            resource.fill_api_io(message.get_data());
+            return send_message(message);
+        }
+
+        /**
+         * Stream the given resource. This resource should be previously requested by an external process.
+         * Otherwise, the resource will not be streamed as nothing will be listening for it.
+         */
+        bool stream(thinger_resource& resource){
+            if(resource.stream_enabled()){
+                stream_resource(resource, thinger_message::STREAM_EVENT);
+                return true;
+            }
+            return false;
+        }
+
+        bool send_message(thinger_message& message)
+        {
+            thinger_encoder sink;
+            sink.encode(message);
+            encoder.pb_encode_varint(MESSAGE);
+            encoder.pb_encode_varint(sink.bytes_written());
+            encoder.encode(message);
+            return write(NULL, 0, true);
+        }
+
+        void handle(unsigned long current_time, bool bytes_available)
+        {
+            // handle input
+            if(bytes_available){
+                handle_input();
+            }
+
+            // handle keep alive
+            if(current_time-last_keep_alive>KEEP_ALIVE_MILLIS){
+                if(keep_alive_response){
+                    last_keep_alive = current_time;
+                    keep_alive_response = false;
+                    encoder.pb_encode_varint(KEEP_ALIVE);
+                    encoder.pb_encode_varint(0);
+                    write(NULL, 0, true);
+                }else{
+                    disconnected();
+                }
+            }
+
+            // handle streaming resources
+            if(thinger_resource::get_streaming_counter()>0){
+                thinger_map<thinger_resource>::entry* current = resources_.begin();
+                while(current!=NULL){
+                    if(current->value_.stream_required(current_time)){
+                        stream_resource(current->value_, thinger_message::STREAM_SAMPLE);
+                    }
+                    current = current->next_;
+                }
+            }
+        }
+
+        bool read_message(thinger_message& message){
+            uint8_t type = decoder.pb_decode_varint32();
+            switch (type){
+                case MESSAGE: {
+                    size_t size = decoder.pb_decode_varint32();
+                    decoder.decode(message, size);
+                }
+                    break;
+                case KEEP_ALIVE: {
+                    size_t size = decoder.pb_decode_varint32();
+                    keep_alive_response = true;
+                }
+                    return false;
+                default:
+                    return false;
+            }
+            return true;
+        }
+
+        bool handle_input(){
+            thinger_message message;
+            if(read_message(message)){
+                handle_request_received(message);
+            }
+            return true;
+        }
+
+    private:
+
+        void handle_request_received(thinger_message& request)
+        {
+            thinger_message response(request);
+            if(!request.has_resource()){
+                response.set_signal_flag(thinger_message::REQUEST_ERROR);
+            }
+            else{
+                thinger_resource * thing_resource = NULL;
+                for(pson_array::iterator it = request.resources().begin(); it.valid(); it.next()){
+                    if(!it.item().is_string()){
+                        response.set_signal_flag(thinger_message::REQUEST_ERROR);
+                        break;
+                    }
+                    const char* resource = it.item();
+
+                    if(it.has_next()){
+                        thing_resource = thing_resource == NULL ? resources_.find(resource) : thing_resource->find(resource);
+                        if(thing_resource==NULL) {
+                            response.set_signal_flag(thinger_message::REQUEST_ERROR);
+                            break;
+                        }
+                    }else{
+                        if(strcmp("api", resource)==0){
+                            if(thing_resource==NULL){
+                                thinger_map<thinger_resource>::entry* current = resources_.begin();
+                                while(current!=NULL){
+                                    current->value_.fill_api(response.get_data()[current->key_]);
+                                    current = current->next_;
+                                }
+                            }else{
+                                thing_resource->fill_api_io(response.get_data());
+                            }
+                        }else{
+                            thing_resource = thing_resource == NULL ? resources_.find(resource) : thing_resource->find(resource);
+                            if(thing_resource==NULL){
+                                response.set_signal_flag(thinger_message::REQUEST_ERROR);
+                            }else{
+                                thing_resource->handle_request(request, response);
+                            }
+                        }
+                    }
+                }
+            }
+            send_message(response);
+        }
+    };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/thinger_decoder.hpp	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,137 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef THINGER_DECODER_HPP
+#define THINGER_DECODER_HPP
+
+#include "pson.h"
+#include "thinger_io.hpp"
+#include "thinger_message.hpp"
+
+namespace thinger{
+
+    class thinger_decoder : public protoson::pson_decoder{
+    public:
+        void decode(thinger_message&  message, size_t size){
+            size_t start_read = bytes_read();
+            while(size-(bytes_read()-start_read)>0) {
+                protoson::pb_wire_type wire_type;
+                uint32_t field_number;
+                pb_decode_tag(wire_type, field_number);
+                switch (wire_type) {
+                    case protoson::length_delimited:{
+                        uint32_t size = pb_decode_varint32();
+                        void *data = NULL;
+                        switch (field_number) {
+                            /*
+                            case thinger_message::THING_ID:
+                                data = protoson::pool.allocate(size + 1);
+                                pb_read_string((char *) data, size);
+                                message.set_thing_id((const char *) data);
+                                break;
+                            */
+                            default:
+                                pb_skip(size);
+                                break;
+                        }
+                    }
+                        break;
+                    case protoson::varint: {
+                        switch (field_number) {
+                            case thinger_message::SIGNAL_FLAG:
+                                message.set_signal_flag((thinger_message::signal_flag)pb_decode_varint32());
+                                break;
+                            case thinger_message::STREAM_ID:
+                                message.set_stream_id(pb_decode_varint32());
+                                break;
+                            default:
+                                pb_skip_varint();
+                                break;
+                        }
+                        break;
+                    }
+                    case protoson::pson_type:
+                        switch(field_number){
+                            case thinger_message::RESOURCE:
+                                protoson::pson_decoder::decode(message.get_resources());
+                                break;
+                            case thinger_message::PSON_PAYLOAD:
+                                protoson::pson_decoder::decode(((protoson::pson&) message));
+                                break;
+                            default:
+                                break;
+                        }
+                        break;
+                    case protoson::fixed_32:
+                        pb_skip(4);
+                        break;
+                    case protoson::fixed_64:
+                        pb_skip(8);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    };
+
+    class thinger_read_decoder : public thinger_decoder{
+    public:
+        thinger_read_decoder(thinger_io& io) : io_(io)
+        {}
+
+    protected:
+        virtual bool read(void* buffer, size_t size){
+            io_.read((char*)buffer, size);
+            protoson::pson_decoder::read(buffer, size);
+            return true;
+        }
+
+    private:
+        thinger_io& io_;
+    };
+
+    class thinger_memory_decoder : public thinger_decoder{
+
+    public:
+        thinger_memory_decoder(uint8_t* buffer, size_t size) : buffer_(buffer), size_(size){}
+
+    protected:
+        virtual bool read(void* buffer, size_t size){
+            if(read_+size<=size_){
+                memcpy(buffer, buffer_ + read_, size);
+                return protoson::pson_decoder::read(buffer, size);
+            }else{
+                return false;
+            }
+        }
+
+    private:
+        uint8_t* buffer_;
+        size_t size_;
+
+    };
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/thinger_encoder.hpp	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,90 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef THINGER_ENCODER_HPP
+#define THINGER_ENCODER_HPP
+
+#include "thinger_message.hpp"
+#include "thinger_io.hpp"
+
+namespace thinger{
+
+class thinger_encoder : public protoson::pson_encoder{
+
+protected:
+    virtual void write(const void *buffer, size_t size){
+        protoson::pson_encoder::write(buffer, size);
+    }
+
+public:
+    void encode(thinger_message& message){
+        if(message.get_stream_id()!=0){
+            pb_encode_varint(thinger_message::STREAM_ID, message.get_stream_id());
+        }
+        if(message.get_signal_flag()!=thinger_message::NONE){
+            pb_encode_varint(thinger_message::SIGNAL_FLAG, message.get_signal_flag());
+        }
+        if(message.has_resource()){
+            pb_encode_tag(protoson::pson_type, thinger_message::RESOURCE);
+            protoson::pson_encoder::encode(message.get_resources());
+        }
+        if(message.has_data()){
+            pb_encode_tag(protoson::pson_type, thinger_message::PSON_PAYLOAD);
+            protoson::pson_encoder::encode((protoson::pson&) message);
+        }
+    }
+};
+
+class thinger_write_encoder : public thinger_encoder{
+    public:
+        thinger_write_encoder(thinger_io& io) : io_(io)
+        {}
+
+    protected:
+        virtual void write(const void *buffer, size_t size){
+            io_.write((const char*)buffer, size);
+            protoson::pson_encoder::write(buffer, size);
+        }
+
+    private:
+        thinger_io& io_;
+};
+
+class thinger_memory_encoder : public thinger_encoder{
+    public:
+        thinger_memory_encoder(uint8_t* buffer, size_t size) : buffer_(buffer), size_(size){}
+
+    protected:
+        virtual void write(const void *buffer, size_t size){
+            memcpy(buffer_ + written_, buffer, size);
+            protoson::pson_encoder::write(buffer, size);
+        }
+
+    private:
+        uint8_t* buffer_;
+        size_t size_;
+};
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/thinger_io.hpp	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,42 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef THINGER_IO_HPP
+#define THINGER_IO_HPP
+
+namespace thinger {
+
+    class thinger_io {
+    public:
+        thinger_io(){}
+        virtual ~thinger_io(){}
+
+    public:
+        virtual bool read(char *buffer, size_t size) = 0;
+        virtual bool write(const char *buffer, size_t size, bool flush = false) = 0;
+    };
+
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/thinger_map.hpp	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,104 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef THINGER_MAP_H
+#define THINGER_MAP_H
+
+#include <string.h>
+
+template <class T>
+class thinger_map {
+
+public:
+    thinger_map() : head_(NULL), last_(NULL) {
+
+    }
+
+    virtual ~thinger_map() {
+    }
+
+public:
+
+    struct entry {
+        entry(const char* key) : key_(key), next_(NULL){
+
+        }
+
+        const char* key_;
+        struct entry * next_;
+        T value_;
+    };
+
+private:
+
+    entry * head_;
+    entry * last_;
+
+public:
+
+    T& operator[](const char* key){
+        entry * current = head_;
+        while(current != NULL){
+            if(strcmp(key, current->key_)==0){
+                return current->value_;
+            }
+            current = current->next_;
+        }
+        // TODO replace with memory allocator for allowing static memory/dynamic memory
+        current = new entry(key);
+
+        if(head_==NULL) head_ = current;
+        if(last_!=NULL) last_->next_ = current;
+        last_ = current;
+        return current->value_;
+    }
+
+    entry* begin(){
+        return head_;
+    }
+
+    entry* end(){
+        return last_;
+    }
+
+    bool empty()
+    {
+        return head_ == last_;
+    }
+
+    T* find(const char* key)
+    {
+        if(key==NULL) return NULL;
+        entry * current = head_;
+        while(current != NULL){
+            if(strcmp(key, current->key_)==0){
+                return &current->value_;
+            }
+            current = current->next_;
+        }
+        return NULL;
+    }
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/thinger_message.hpp	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,180 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef THINGER_MESSAGE_HPP
+#define THINGER_MESSAGE_HPP
+
+#include "pson.h"
+
+namespace thinger{
+
+    enum message_type{
+        MESSAGE             = 1,
+        KEEP_ALIVE          = 2
+    };
+
+    class thinger_message{
+
+    public:
+
+        // fields for a thinger message (encoded as in protocol buffers)
+        enum fields{
+            STREAM_ID       = 1,
+            SIGNAL_FLAG     = 2,
+            UNUSED          = 3,
+            RESOURCE        = 4,
+            UNUSED2         = 5,
+            PSON_PAYLOAD    = 6
+        };
+
+        // flags for describing a thinger message
+        enum signal_flag {
+            // GENERAL USED FLAGS
+            REQUEST_OK          = 1,    // the request with the given stream id was successful
+            REQUEST_ERROR       = 2,    // the request with the given stream id failed
+
+            // SENT BY THE SERVER
+            NONE                = 0,    // default resource action: just execute the given resource
+            START_STREAM        = 3,    // enable a streaming resource (with stream_id, and resource filled, sample interval (in payload) is optional)
+            STOP_STREAM         = 4,    // stop the streaming resource (with stream_id, and resource filled)
+
+            // SENT BY DEVICE
+            AUTH                = 5,
+            STREAM_EVENT        = 6,    // means that the message data is related to a stream event
+            STREAM_SAMPLE       = 7,    // means that the message is related to a periodical streaming sample
+            CALL_ENDPOINT       = 8     // call the endpoint with the provided name (endpoint in resource, value passed in payload)
+        };
+
+    public:
+
+        /**
+         * Initialize a default response  message setting the same stream id of the source message,
+         * and initializing the signal flag to ok. All remaining data or fields are empty
+         */
+        thinger_message(thinger_message& other) :
+            stream_id(other.stream_id),
+            flag(REQUEST_OK),
+            resource(NULL),
+            data(NULL),
+            data_allocated(false)
+        {}
+
+        /**
+         * Initialize a default empty message
+         */
+        thinger_message() :
+                stream_id(0),
+                flag(NONE),
+                resource(NULL),
+                data(NULL),
+                data_allocated(false)
+        {}
+
+        ~thinger_message(){
+            // deallocate resource
+            destroy(resource, protoson::pool);
+            // deallocate paylaod if was allocated here
+            if(data_allocated){
+                destroy(data, protoson::pool);
+            }
+        }
+
+    private:
+        /// used for identifying a unique stream
+        uint16_t stream_id;
+        /// used for setting a stream signal
+        signal_flag flag;
+        /// used to identify a device resource
+        protoson::pson* resource;
+        /// used to fill a data payload in the message
+        protoson::pson* data;
+        /// flag to determine when the payload has been reserved
+        bool data_allocated;
+
+    public:
+
+        uint16_t get_stream_id(){
+            return stream_id;
+        }
+
+        signal_flag get_signal_flag(){
+            return flag;
+        }
+
+        bool has_data(){
+            return data!=NULL;
+        }
+
+        bool has_resource(){
+            return resource!=NULL;
+        }
+
+    public:
+        void set_stream_id(uint16_t stream_id) {
+            thinger_message::stream_id = stream_id;
+        }
+
+        void set_signal_flag(signal_flag const &flag) {
+            thinger_message::flag = flag;
+        }
+
+    public:
+
+        void operator=(const char* str){
+            ((protoson::pson &) * this) = str;
+        }
+
+        operator protoson::pson&(){
+            if(data==NULL){
+                data = new (protoson::pool) protoson::pson;
+                data_allocated = true;
+            }
+            return *data;
+        }
+
+        protoson::pson_array& resources(){
+            return (protoson::pson_array&)get_resources();
+        }
+
+        protoson::pson& get_resources(){
+            if(resource==NULL){
+                resource = new (protoson::pool) protoson::pson;
+            }
+            return *resource;
+        }
+
+        protoson::pson& get_data(){
+            return *this;
+        }
+
+        void set_data(protoson::pson& pson_data){
+            if(data==NULL){
+                data = &pson_data;
+                data_allocated = false;
+            }
+        }
+
+    };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/thinger_resource.hpp	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,275 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 THINGER LTD
+// Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef THINGER_RESOURCE_HPP
+#define THINGER_RESOURCE_HPP
+
+#include "thinger_map.hpp"
+#include "pson.h"
+#include "thinger_message.hpp"
+
+namespace thinger{
+
+
+class thinger_resource {
+
+public:
+    enum io_type {
+        none                = 0,
+        run                 = 1,
+        pson_in             = 2,
+        pson_out            = 3,
+        pson_in_pson_out    = 4
+    };
+
+    enum access_type{
+        PRIVATE     = 0,
+        PROTECTED   = 1,
+        PUBLIC      = 2,
+        NONE        = 3
+    };
+
+    static int get_streaming_counter(){
+        return streaming_count_;
+    }
+
+private:
+
+    // calback for function, input, output, or input/output
+    union callback{
+        void (*run)();
+        void (*pson_in)(protoson::pson& in);
+        void (*pson_out)(protoson::pson& out);
+        void (*pson_in_pson_out)(protoson::pson& in, protoson::pson& out);
+    };
+
+    // used for defining the resource
+    io_type io_type_;
+    access_type access_type_;
+    callback callback_;
+
+    // used for allowing resource streaming (both periodically or by events)
+    uint16_t stream_id_;
+
+    // used for periodic stream events
+    unsigned long streaming_freq_;
+    unsigned long last_streaming_;
+
+    // used to know the total number of streams
+    static unsigned int streaming_count_;
+
+    // TODO change to pointer so it is not using more than a pointer size if not used?
+    thinger_map<thinger_resource> sub_resources_;
+
+    void enable_streaming(uint16_t stream_id, unsigned long streaming_freq){
+        stream_id_ = stream_id;
+        streaming_freq_ = streaming_freq;
+        last_streaming_ = 0;
+        streaming_count_++;
+    }
+
+public:
+    thinger_resource() : io_type_(none), access_type_(PRIVATE), stream_id_(0), streaming_freq_(0), last_streaming_(0)
+    {}
+
+    void disable_streaming(){
+        stream_id_ = 0;
+        streaming_freq_ = 0;
+        streaming_count_--;
+    }
+
+    bool stream_enabled(){
+        return stream_id_ > 0;
+    }
+
+    uint32_t get_stream_id(){
+        return stream_id_;
+    }
+
+    bool stream_required(unsigned long timestamp){
+        // sample interval is activated
+        if(streaming_freq_>0){
+            if(timestamp-last_streaming_>=streaming_freq_){
+                last_streaming_ = timestamp;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    thinger_resource * find(const char* res)
+    {
+        return sub_resources_.find(res);
+    }
+
+    thinger_resource & operator[](const char* res){
+        return sub_resources_[res];
+    }
+
+    thinger_resource & operator()(access_type type){
+        access_type_ = type;
+        return *this;
+    }
+
+    io_type get_io_type(){
+        return io_type_;
+    }
+
+    access_type get_access_type(){
+        return access_type_;
+    }
+
+    void fill_api(protoson::pson_object& content){
+        if(io_type_!=none){
+            content["al"] = access_type_;
+            content["fn"] = io_type_;
+        }
+        thinger_map<thinger_resource>::entry* current = sub_resources_.begin();
+        if(current!=NULL){
+            protoson::pson_object& actions = content["/"];
+            do{
+                current->value_.fill_api(actions[current->key_]);
+                current = current->next_;
+            }while(current!=NULL);
+        }
+    }
+
+    void fill_api_io(protoson::pson_object& content){
+        if(io_type_ == pson_in){
+            callback_.pson_in(content["in"]);
+        }else if(io_type_ == pson_out){
+            callback_.pson_out(content["out"]);
+        }else if(io_type_ == pson_in_pson_out){
+            callback_.pson_in_pson_out(content["in"], content["out"]);
+        }
+    }
+
+public:
+
+    /**
+     * Establish a function without input or output parameters
+     */
+    void operator=(void (*run_function)()){
+        io_type_ = run;
+        callback_.run = run_function;
+    }
+
+    /**
+     * Establish a function without input or output parameters
+     */
+    void set_function(void (*run_function)()){
+        io_type_ = run;
+        callback_.run = run_function;
+    }
+
+    /**
+     * Establish a function with input parameters
+     */
+    void operator<<(void (*in_function)(protoson::pson& in)){
+        io_type_ = pson_in;
+        callback_.pson_in = in_function;
+    }
+
+    /**
+     * Establish a function with input parameters
+     */
+    void set_input(void (*in_function)(protoson::pson& in)){
+        io_type_ = pson_in;
+        callback_.pson_in = in_function;
+    }
+
+    /**
+     * Establish a function that only generates an output
+     */
+    void operator>>(void (*out_function)(protoson::pson& out)){
+        io_type_ = pson_out;
+        callback_.pson_out = out_function;
+    }
+
+    /**
+     * Establish a function that only generates an output
+     */
+    void set_output(void (*out_function)(protoson::pson& out)){
+        io_type_ = pson_out;
+        callback_.pson_out = out_function;
+    }
+
+    /**
+     * Establish a function that can receive input parameters and generate an output
+     */
+    void operator=(void (*pson_in_pson_out_function)(protoson::pson& in, protoson::pson& out)){
+        io_type_ = pson_in_pson_out;
+        callback_.pson_in_pson_out = pson_in_pson_out_function;
+    }
+
+    /**
+     * Establish a function that can receive input parameters and generate an output
+     */
+    void set_input_output(void (*pson_in_pson_out_function)(protoson::pson& in, protoson::pson& out)){
+        io_type_ = pson_in_pson_out;
+        callback_.pson_in_pson_out = pson_in_pson_out_function;
+    }
+
+    /**
+     * Handle a request and fill a possible response
+     */
+    void handle_request(thinger_message& request, thinger_message& response){
+        switch(request.get_signal_flag()){
+            // default action over the stream (run the resource)
+            case thinger_message::NONE:
+                switch (io_type_){
+                    case run:
+                        callback_.run();
+                        break;
+                    case pson_in:
+                        callback_.pson_in(request);
+                        break;
+                    case pson_out:
+                        callback_.pson_out(response);
+                        break;
+                    case pson_in_pson_out:
+                        callback_.pson_in_pson_out(request, response);
+                        break;
+                    case none:
+                        break;
+                }
+                break;
+            // flag for starting a resource stream
+            case thinger_message::START_STREAM:
+                enable_streaming(request.get_stream_id(), request.get_data());
+                break;
+            // flat for stopping a resource stream
+            case thinger_message::STOP_STREAM:
+                disable_streaming();
+                break;
+            default:
+                break;
+        }
+    }
+};
+
+    unsigned int thinger_resource::streaming_count_ = 0;
+
+}
+
+#endif