Fork of EnvoyNespressoEndpoint that uses ColorDetectorV2

Dependencies:   ColorDetectorV2 ExternalFlashClient SNICInterface mbed-rtos mbed nsdl

Fork of EnvoyNespressoEndpoint by Nespresso RGB Sensor

Files at this revision

API Documentation at this revision

Comitter:
bridadan
Date:
Wed Apr 15 19:31:33 2015 +0000
Child:
1:10534b1c83d4
Commit message:
Initial commit

Changed in this revision

ColorDetector/ColorDetector.cpp Show annotated file Show diff for this revision Revisions of this file
ColorDetector/ColorDetector.h Show annotated file Show diff for this revision Revisions of this file
ColorDetector/GroveColourSensor.lib Show annotated file Show diff for this revision Revisions of this file
EthernetInterface.lib Show annotated file Show diff for this revision Revisions of this file
dbg.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
nsdl_lib.lib Show annotated file Show diff for this revision Revisions of this file
nsdl_support.cpp Show annotated file Show diff for this revision Revisions of this file
nsdl_support.h Show annotated file Show diff for this revision Revisions of this file
resources/color_detector.cpp Show annotated file Show diff for this revision Revisions of this file
resources/color_detector.h Show annotated file Show diff for this revision Revisions of this file
resources/payload.cpp Show annotated file Show diff for this revision Revisions of this file
resources/payload.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ColorDetector/ColorDetector.cpp	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,90 @@
+#include "ColorDetector.h"
+
+/**
+*    Constructor.
+*
+*    @param sensor Pointer to instantiated and powered up Grove colour sensor.
+*    @param threshold Total change in channel values needed to trigger a "change" event.
+*        This will start to save samples for later use until the total change in channel values dips below this threshold.
+*    @param samplesPerReading Specifies the number of samples to take per sample() call.
+*/
+ColorDetector::ColorDetector(GroveColourSensor *sensor, uint16_t threshold, uint16_t samplesPerReading)
+    :sensor(sensor), threshold(threshold), changeDetected(false), curBuffPointer(0),
+    samplesPerReading(samplesPerReading) {
+        // Empty
+}
+
+
+/**
+*    Takes the specified number of samples and returns the average channel values.
+*
+*    @param numSamples Number of samples to take.
+*/
+RGBC ColorDetector::takeSamples(uint16_t numSamples) {
+    uint32_t tot_r = 0;
+    uint32_t tot_g = 0;
+    uint32_t tot_b = 0;
+    uint32_t tot_c = 0;
+    
+    for (uint32_t i = curBuffPointer; (i < curBuffPointer + numSamples) && (i < CD_BUFF_LEN); i++) {
+        sensor->readBlock(sample_buf + i);
+        tot_r += sample_buf[i].ch.red;
+        tot_g += sample_buf[i].ch.green;
+        tot_b += sample_buf[i].ch.blue;
+        tot_c += sample_buf[i].ch.clear;
+    }
+    
+    curBuffPointer += numSamples;
+    
+    RGBC ret;
+    ret.ch.red = tot_r / numSamples;
+    ret.ch.green = tot_g / numSamples;
+    ret.ch.blue = tot_b / numSamples;
+    ret.ch.clear = tot_c / numSamples;
+    
+    return ret;
+}
+
+/**
+*    This establishes the average "resting" channel values. This should be called before calling sample().
+*/
+void ColorDetector::setBaseline() {
+    baselineAvg = takeSamples(samplesPerReading);
+}
+
+/**
+*    Takes samples from sensor, keeping track if the change in color rises above the threshold.
+*    If a change is detected and then returns to the baseline average or it reaches the max number of samples,
+*    this will return a value great than 0 that indicates how many samples are in the buffer.
+*    If no change is detected, 0 is returned.
+*/
+int ColorDetector::sample() {
+    int retVal = 0;
+    RGBC avg = takeSamples(samplesPerReading);
+    
+    uint32_t totalDiff = 0;
+    
+    for (int i = 0; i < 4; i++) {
+        totalDiff += abs((int)avg.data[i] - baselineAvg.data[i]);
+    }
+    
+    if (totalDiff > threshold && curBuffPointer < CD_BUFF_LEN) {
+        changeDetected = true;
+    } else {
+        if (changeDetected) {
+            retVal = curBuffPointer;
+        }
+        
+        changeDetected = false;
+        curBuffPointer = 0;
+    }
+    
+    return retVal;
+}
+
+/**
+*    Returns a pointer to the sample buffer (does not change).
+*/
+RGBC* ColorDetector::getBuffer() {
+    return sample_buf;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ColorDetector/ColorDetector.h	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,29 @@
+#ifndef _COLOR_DETECTOR_H_
+#define _COLOR_DETECTOR_H_
+
+#include "mbed.h"
+#include "GroveColourSensor.h"
+
+#define CD_BUFF_LEN 512
+
+
+class ColorDetector {
+public:
+    ColorDetector(GroveColourSensor *sensor, uint16_t threshold);
+    void setBaseline();
+    int sample();
+    RGBC* getBuffer();
+    
+private:    
+    RGBC sample_buf[CD_BUFF_LEN];
+    GroveColourSensor *sensor;
+    uint16_t threshold;
+    RGBC baselineAvg;
+    uint32_t curBuffPointer;
+    bool changeDetected;
+    uint16_t samplesPerReading;
+    
+    RGBC takeSamples(uint16_t numSamples);
+};
+    
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ColorDetector/GroveColourSensor.lib	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,1 @@
+https://developer.mbed.org/users/bridadan/code/GroveColourSensor/#50cb56828ab9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EthernetInterface.lib	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,1 @@
+EthernetInterface#2fc406e2553f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbg.h	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,17 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include "nsdl_support.h"
+#include "mbed.h"
+
+//Debug is disabled by default
+#define DEBUG 1
+
+#if (DEBUG)
+extern Serial pc;
+#define NSDL_DEBUG(x, ...) pc.printf("[NSDL_DEBUG: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
+#else
+#define NSDL_DEBUG(x, ...)
+#endif
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,164 @@
+#include "mbed.h"
+#include "EthernetInterface.h"
+#include "nsdl_support.h"
+#include "dbg.h"
+#include "GroveColourSensor.h"
+#include "ColorDetector.h"
+#include "color_detector.h"
+
+Serial pc(USBTX, USBRX); // tx, rx
+
+// ****************************************************************************
+// Configuration section
+
+// Ethernet configuration
+/* Define this to enable DHCP, otherwise manual address configuration is used */
+#define DHCP
+
+/* Manual IP configurations, if DHCP not defined */
+#define IP      "0.0.0.0"
+#define MASK    "255.255.255.0"
+#define GW      "0.0.0.0"
+
+// NSP configuration
+/* Change this IP address to that of your mbed Device Server installation */
+static const char* NSP_ADDRESS = "10.112.30.70";
+static const int NSP_PORT = 5683;
+char endpoint_name[] = "nespresso-client";
+uint8_t ep_type[] = {"mbed_device"};
+uint8_t lifetime_ptr[] = {"1200"};
+
+// ****************************************************************************
+// Ethernet initialization
+
+EthernetInterface eth;
+
+static void ethernet_init()
+{
+    // Initialize network
+#ifdef DHCP
+    NSDL_DEBUG("DHCP in use\r\n");
+    eth.init();
+#else
+    eth.init(IP, MASK, GW);
+#endif
+    if(eth.connect(30000) == 0)
+        pc.printf("Connect OK\n\r");
+
+    NSDL_DEBUG("IP Address:%s ", eth.getIPAddress());
+}
+
+// ****************************************************************************
+// NSP initialization
+
+UDPSocket server;
+Endpoint nsp;
+
+static void nsp_init()
+{
+    server.init();
+    server.bind(NSP_PORT);
+
+    nsp.set_address(NSP_ADDRESS, NSP_PORT);
+    
+    NSDL_DEBUG("name: %s", endpoint_name);
+    NSDL_DEBUG("NSP=%s - port %d\n", NSP_ADDRESS, NSP_PORT);
+}
+
+// ****************************************************************************
+// Resource creation
+
+static int create_resources()
+{
+    sn_nsdl_resource_info_s *resource_ptr = NULL;
+    sn_nsdl_ep_parameters_s *endpoint_ptr = NULL;
+    
+    NSDL_DEBUG("Creating resources");
+
+    // Create resources
+    resource_ptr = (sn_nsdl_resource_info_s*)nsdl_alloc(sizeof(sn_nsdl_resource_info_s));
+    if(!resource_ptr)
+        return 0;
+    memset(resource_ptr, 0, sizeof(sn_nsdl_resource_info_s));
+
+    resource_ptr->resource_parameters_ptr = (sn_nsdl_resource_parameters_s*)nsdl_alloc(sizeof(sn_nsdl_resource_parameters_s));
+    if(!resource_ptr->resource_parameters_ptr)
+    {
+        nsdl_free(resource_ptr);
+        return 0;
+    }
+    memset(resource_ptr->resource_parameters_ptr, 0, sizeof(sn_nsdl_resource_parameters_s));
+
+    // Dynamic resources
+    create_color_detector_resource(resource_ptr);
+    
+    // Register with NSP
+    endpoint_ptr = nsdl_init_register_endpoint(endpoint_ptr, (uint8_t*)endpoint_name, ep_type, lifetime_ptr);
+    if(sn_nsdl_register_endpoint(endpoint_ptr) != 0)
+        pc.printf("NSP registering failed\r\n");
+    else
+        pc.printf("NSP registering OK\r\n");
+    nsdl_clean_register_endpoint(&endpoint_ptr);
+
+    nsdl_free(resource_ptr->resource_parameters_ptr);
+    nsdl_free(resource_ptr);
+    return 1;
+}
+
+// ****************************************************************************
+// Program entry point
+
+int main()
+{
+    NSDL_DEBUG("mbed Nespresso Demo\n");
+    
+    // Begin Color Sensor init
+    
+    // First, ensure I2C bus is released by toggling clock (fixes reset errors)
+    DigitalOut scl_dummy(I2C_SCL);
+    
+    for (int i = 0; i < 100; i++) {
+        scl_dummy = !scl_dummy;
+        wait_us(2);
+    }
+    
+    // Next, initialize I2C
+    I2C i2c(I2C_SDA, I2C_SCL);
+    
+    // Create color sensor instance
+    GroveColourSensor colorSensor(&i2c);
+    
+    // Various config
+    colorSensor.powerUp();
+    colorSensor.setGain(1);
+    colorSensor.setBlockRead();
+    
+    // Create color detector
+    ColorDetector detector(&colorSensor, 50);
+    detector.setBaseline();
+    
+    // Pass reference to color detector
+    set_color_detector(&detector);
+    
+    // Initialize Ethernet interface first
+    ethernet_init();
+    
+    // Initialize NSP node
+    nsp_init();
+    
+    // Initialize NSDL stack
+    nsdl_init();
+    
+    // Create NSDL resources
+    create_resources();
+    
+    nsdl_event_loop_init();
+    
+    while(true) {
+        // Check if any nsdl events need to be handled
+        nsdl_event_loop_run_once();
+        
+        // Sample the color detector and potentially send samples to mDS
+        run_color_detector();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,1 @@
+mbed-rtos#d3d0e710b443
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/433970e64889
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nsdl_lib.lib	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,1 @@
+nsdl_lib#388450b1e776
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nsdl_support.cpp	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,175 @@
+// NSDL library support functions
+
+#include "mbed.h"
+#include "nsdl_support.h"
+#include "mbed.h"
+#include "rtos.h"
+#include "EthernetInterface.h"
+
+extern Serial pc;
+extern EthernetInterface eth;
+extern Endpoint nsp;
+extern UDPSocket server;
+extern char endpoint_name[16];
+extern uint8_t ep_type[];
+extern uint8_t lifetime_ptr[];
+
+// Registration update variables
+sn_nsdl_ep_parameters_s *endpoint_ptr = NULL;
+Ticker registration_updater;
+bool update_registration = false;
+
+// NSDL loop variables
+sn_nsdl_addr_s received_packet_address;
+uint8_t received_address[4];
+char buffer[1024];
+Endpoint from;
+
+/* The number of seconds between NSP registration messages */
+#define RD_UPDATE_PERIOD  60
+
+void *nsdl_alloc(uint16_t size)
+{
+    return malloc(size);
+}
+
+void nsdl_free(void* ptr_to_free)
+{
+    free(ptr_to_free);
+}
+
+/*
+ * Create a static resoure
+ * Only get is allowed
+ */
+void nsdl_create_static_resource(sn_nsdl_resource_info_s *resource_structure, uint16_t pt_len, uint8_t *pt, uint16_t rpp_len, uint8_t *rpp_ptr, uint8_t *rsc, uint16_t rsc_len)
+{
+    resource_structure->access = SN_GRS_GET_ALLOWED;
+    resource_structure->mode = SN_GRS_STATIC;
+    resource_structure->pathlen = pt_len;
+    resource_structure->path = pt;
+    resource_structure->resource_parameters_ptr->resource_type_len = rpp_len;
+    resource_structure->resource_parameters_ptr->resource_type_ptr = rpp_ptr;
+    resource_structure->resource = rsc;
+    resource_structure->resourcelen = rsc_len;
+    sn_nsdl_create_resource(resource_structure);
+}
+
+void nsdl_create_dynamic_resource(sn_nsdl_resource_info_s *resource_structure, uint16_t pt_len, uint8_t *pt, uint16_t rpp_len, uint8_t *rpp_ptr, uint8_t is_observable, sn_grs_dyn_res_callback_t callback_ptr, int access_right)
+{
+    resource_structure->access = (sn_grs_resource_acl_e)access_right;
+    resource_structure->resource = 0;
+    resource_structure->resourcelen = 0;
+    resource_structure->sn_grs_dyn_res_callback = callback_ptr;
+    resource_structure->mode = SN_GRS_DYNAMIC;
+    resource_structure->pathlen = pt_len;
+    resource_structure->path = pt;
+    resource_structure->resource_parameters_ptr->resource_type_len = rpp_len;
+    resource_structure->resource_parameters_ptr->resource_type_ptr = rpp_ptr;
+    resource_structure->resource_parameters_ptr->observable = is_observable;
+    sn_nsdl_create_resource(resource_structure);
+}
+
+sn_nsdl_ep_parameters_s* nsdl_init_register_endpoint(sn_nsdl_ep_parameters_s *endpoint_structure, uint8_t* name, uint8_t* typename_ptr, uint8_t *lifetime_ptr)
+{
+    if (NULL == endpoint_structure)
+    {   
+        endpoint_structure = (sn_nsdl_ep_parameters_s*)nsdl_alloc(sizeof(sn_nsdl_ep_parameters_s));
+    }
+    if (endpoint_structure)
+    {
+        memset(endpoint_structure, 0, sizeof(sn_nsdl_ep_parameters_s));
+        endpoint_structure->endpoint_name_ptr = name;
+        endpoint_structure->endpoint_name_len = strlen((char*)name);
+        endpoint_structure->type_ptr = typename_ptr;
+        endpoint_structure->type_len =  strlen((char*)typename_ptr);
+        endpoint_structure->lifetime_ptr = lifetime_ptr;
+        endpoint_structure->lifetime_len =  strlen((char*)lifetime_ptr);
+    }
+    return endpoint_structure;
+}
+
+void nsdl_clean_register_endpoint(sn_nsdl_ep_parameters_s **endpoint_structure)
+{
+    if (*endpoint_structure)
+    {
+        nsdl_free(*endpoint_structure);
+        *endpoint_structure = NULL;
+    }
+}
+
+static uint8_t tx_cb(sn_nsdl_capab_e protocol, uint8_t *data_ptr, uint16_t data_len, sn_nsdl_addr_s *address_ptr)
+{
+    pc.printf("TX callback!\n\rSending %d bytes\r\n", data_len);
+
+    if(server.sendTo(nsp, (char*)data_ptr, data_len) != data_len)
+        pc.printf("sending failed\n\r");
+
+    return 1;
+}
+
+static uint8_t rx_cb(sn_coap_hdr_s *coap_packet_ptr, sn_nsdl_addr_s *address_ptr)
+{
+    pc.printf("RX callback!\r\n");
+    return 0;
+}
+
+void registration_ticker_func() {
+    update_registration = true;
+}
+
+static void registration_update()
+{
+    endpoint_ptr = nsdl_init_register_endpoint(endpoint_ptr, (uint8_t*)endpoint_name, ep_type, lifetime_ptr);
+    if(sn_nsdl_register_endpoint(endpoint_ptr) != 0)
+        pc.printf("NSP re-registering failed\r\n");
+    else
+        pc.printf("NSP re-registering OK\r\n");
+    nsdl_clean_register_endpoint(&endpoint_ptr);
+}
+
+void nsdl_init()
+{
+    uint8_t nsp_addr[4];
+    sn_nsdl_mem_s memory_cbs;
+
+    /* Initialize libNsdl */
+    memory_cbs.sn_nsdl_alloc = &nsdl_alloc;
+    memory_cbs.sn_nsdl_free = &nsdl_free;
+    if(sn_nsdl_init(&tx_cb, &rx_cb, &memory_cbs) == -1)
+        pc.printf("libNsdl init failed\r\n");
+    else
+        pc.printf("libNsdl init done\r\n");
+
+    /* Set nsp address for library */
+    set_NSP_address(nsp_addr, 5683, SN_NSDL_ADDRESS_TYPE_IPV4);    
+}
+
+void nsdl_event_loop_init() {
+    server.set_blocking(false, 200);
+    memset(&received_packet_address, 0, sizeof(sn_nsdl_addr_s));
+    received_packet_address.addr_ptr = received_address;
+    
+    registration_updater.attach(&registration_ticker_func, RD_UPDATE_PERIOD);
+}
+
+void nsdl_event_loop_run_once()
+{
+    if (update_registration) {
+        update_registration = false;
+        registration_update();
+    }
+    
+    int n = server.receiveFrom(from, buffer, sizeof(buffer));
+    
+    if (n < 0)
+    {
+        pc.printf("Socket error\n\r");
+    }
+    else
+    {   
+        sn_nsdl_process_coap((uint8_t*)buffer, n, &received_packet_address);
+    }
+}
+
+            
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nsdl_support.h	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,25 @@
+// Support functions for the NSDL library
+
+#ifndef NSDL_SUPPORT_H
+#define NSDL_SUPPORT_H
+
+#include "mbed.h"
+#include <stdint.h>
+#include "sn_nsdl.h"
+#include "sn_coap_header.h"
+#include "sn_coap_protocol.h"
+#include "sn_nsdl_lib.h"
+
+typedef uint8_t (*sn_grs_dyn_res_callback_t)(sn_coap_hdr_s *, sn_nsdl_addr_s *, sn_proto_info_s *);
+
+extern "C" void *nsdl_alloc(uint16_t size);
+extern "C" void nsdl_free(void* ptr_to_free);
+void nsdl_create_static_resource(sn_nsdl_resource_info_s *resource_structure, uint16_t pt_len, uint8_t *pt, uint16_t rpp_len, uint8_t *rpp_ptr, uint8_t *rsc, uint16_t rsc_len);
+void nsdl_create_dynamic_resource(sn_nsdl_resource_info_s *resource_structure, uint16_t pt_len, uint8_t *pt, uint16_t rpp_len, uint8_t *rpp_ptr, uint8_t is_observable, sn_grs_dyn_res_callback_t callback_ptr, int access_right);
+sn_nsdl_ep_parameters_s* nsdl_init_register_endpoint(sn_nsdl_ep_parameters_s *endpoint_structure, uint8_t* name, uint8_t* ypename_ptr, uint8_t *lifetime_ptr);
+void nsdl_clean_register_endpoint(sn_nsdl_ep_parameters_s **endpoint_structure);
+void nsdl_init();
+void nsdl_event_loop_init();
+void nsdl_event_loop_run_once();
+
+#endif // NSDL_SUPPORT_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/color_detector.cpp	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,89 @@
+#include "mbed.h"
+#include "nsdl_support.h"
+#include "payload.h"
+#include "ColorDetector.h"
+
+#define RESOURCE_ID    "sensor/capsule"
+
+extern Serial pc;
+/* stored data for observable resource */
+static uint8_t obs_number = 0;
+static uint8_t *obs_token_ptr = NULL;
+static uint8_t obs_token_len = 0;
+
+ColorDetector *g_detector;
+static Payload g_payload;
+
+void set_color_detector(ColorDetector *detector) {
+    g_detector = detector;
+}
+
+void run_color_detector()
+{
+    if (obs_number == 0 && obs_token_ptr == NULL)
+        pc.printf("ERROR: obs_number: %d, obs_token_ptr: %0x%08x\r\n", obs_number, obs_token_ptr);
+
+    obs_number++;
+    
+    // Take samples from color detector
+    int samples_captured = g_detector->sample();
+    
+    // If a capsule was detected, a number greater than 0 is returned
+    if (samples_captured > 0) {
+        // Build payload from color detector buffer
+        g_payload.build(g_detector->getBuffer(), samples_captured);
+        
+        // Send payload to mDS
+        int result = sn_nsdl_send_observation_notification(obs_token_ptr, obs_token_len, g_payload.raw_bytes(), g_payload.raw_bytes_size(), &obs_number, 1, COAP_MSG_TYPE_NON_CONFIRMABLE, 0);
+        
+        pc.printf("Observation send %s. [samples: %d, bytes: %d]\r\n", result == 0 ? "failed" : "succeeded", samples_captured, g_payload.raw_bytes_size());
+    }      
+}
+
+/* Only GET method allowed */
+static uint8_t resource_cb(sn_coap_hdr_s *received_coap_ptr, sn_nsdl_addr_s *address, sn_proto_info_s * proto)
+{
+    sn_coap_hdr_s *coap_res_ptr = 0;
+
+    coap_res_ptr = sn_coap_build_response(received_coap_ptr, COAP_MSG_CODE_RESPONSE_CONTENT);
+
+    coap_res_ptr->payload_len = g_payload.raw_bytes_size();
+    coap_res_ptr->payload_ptr = g_payload.raw_bytes();
+
+    if(received_coap_ptr->token_ptr)
+    {
+        if(obs_token_ptr)
+        {
+            free(obs_token_ptr);
+            obs_token_ptr = 0;
+        }
+        obs_token_ptr = (uint8_t*)malloc(received_coap_ptr->token_len);
+        if(obs_token_ptr)
+        {
+            memcpy(obs_token_ptr, received_coap_ptr->token_ptr, received_coap_ptr->token_len);
+            obs_token_len = received_coap_ptr->token_len;
+        }
+    }
+
+    if(received_coap_ptr->options_list_ptr->observe)
+    {
+        coap_res_ptr->options_list_ptr = (sn_coap_options_list_s*)malloc(sizeof(sn_coap_options_list_s));
+        memset(coap_res_ptr->options_list_ptr, 0, sizeof(sn_coap_options_list_s));
+        coap_res_ptr->options_list_ptr->observe_ptr = &obs_number;
+        coap_res_ptr->options_list_ptr->observe_len = 1;
+        obs_number++;
+    }
+
+    sn_nsdl_send_coap_message(address, coap_res_ptr);
+
+    coap_res_ptr->options_list_ptr->observe_ptr = 0;
+    sn_coap_parser_release_allocated_coap_msg_mem(coap_res_ptr);
+
+    return 0;
+}
+
+int create_color_detector_resource(sn_nsdl_resource_info_s *resource_ptr)
+{
+    nsdl_create_dynamic_resource(resource_ptr, sizeof(RESOURCE_ID) - 1, (uint8_t*)RESOURCE_ID, 0, 0, 1, &resource_cb, SN_GRS_GET_ALLOWED);
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/color_detector.h	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,13 @@
+// Nespresso resource implementation
+
+#ifndef _COLOR_DETECTOR_RESOURCE_H_
+#define _COLOR_DETECTOR_RESOURCE_H_
+
+#include "ColorDetector.h"
+#include "nsdl_support.h"
+
+void set_color_detector(ColorDetector *detector);
+void run_color_detector();
+int create_color_detector_resource(sn_nsdl_resource_info_s *resource_ptr);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/payload.cpp	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,78 @@
+/*
+ * Builds the payload to send to the server.
+ *
+ * Copyright (c) 2014 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <mbed.h>
+#include "payload.h"
+
+// Maximum number of samples to include in the payload.
+// Defined such that the payload fits within one UDP packet.
+#define MAX_SAMPLES 300
+
+Payload::Payload() :
+    m_mdb_bytes(0)
+{
+}
+
+//
+// Build the payload. The payload is defined by the Payload::MeanDeltaBuffer struct 
+// and consists of: 
+//   * number of samples (32 bits)
+//   * mean of red, green, blue channels (32 bits each)
+//   * difference of sample to mean for red, green, blue channels (1 byte each)   
+//
+// This encoding can store ~300 samples in a one UDP packet.
+//
+void Payload::build(const RGBC* samples, int sample_count)
+{
+    if (sample_count > MAX_SAMPLES)
+        sample_count = MAX_SAMPLES;
+        
+    for (int i = 0; i < sample_count; i++)
+    {
+        m_mdb.mean[0] += (samples[i].data[0]);
+        m_mdb.mean[1] += (samples[i].data[1]);
+        m_mdb.mean[2] += (samples[i].data[2]);
+    }
+    
+    m_mdb.sample_count = sample_count;
+    m_mdb.mean[0] /= sample_count;
+    m_mdb.mean[1] /= sample_count;
+    m_mdb.mean[2] /= sample_count;
+    
+    for (int i = 0; i < sample_count; i++)
+    {
+        // TODO: clamp to signed char range [-128, 127]
+        m_mdb.deltas[i][0] = m_mdb.mean[0] - (samples[i].data[0]);
+        m_mdb.deltas[i][1] = m_mdb.mean[1] - (samples[i].data[1]);
+        m_mdb.deltas[i][2] = m_mdb.mean[2] - (samples[i].data[2]);
+    }    
+
+    // calculate number of bytes in payload    
+    m_mdb_bytes = sizeof(int) + // number of samples
+        (3 * sizeof(int)) + // mean of red, green & blue channels
+        (sample_count * sizeof(signed char) * 3); // one byte per channel per sample
+}
+
+int Payload::raw_bytes_size() 
+{ 
+    return m_mdb_bytes; 
+}
+
+uint8_t* Payload::raw_bytes() const 
+{ 
+    return (uint8_t *)&m_mdb; 
+} 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/payload.h	Wed Apr 15 19:31:33 2015 +0000
@@ -0,0 +1,49 @@
+/*
+ * Builds the payload to send to the server.
+ *
+ * Copyright (c) 2014 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#ifndef _PAYLOAD_H
+#define _PAYLOAD_H
+#include "GroveColourSensor.h"
+
+// TODO: include from common file
+#ifndef RGB_VALUES
+#define RGB_VALUES      512
+#endif
+
+class Payload
+{
+public:
+    Payload();
+    
+    void build(const RGBC *samples, int sample_count);
+    int raw_bytes_size();
+    uint8_t* raw_bytes() const;
+    
+private:
+    typedef struct 
+    {
+        int sample_count;
+        int mean[3];
+        signed char deltas[RGB_VALUES][3];
+    } MeanDeltaBuffer;
+
+    MeanDeltaBuffer m_mdb;
+    int m_mdb_bytes;
+};
+
+#endif // _PAYLOAD_H
\ No newline at end of file