A simple CIoT message protocol, used in the Water Meter Demos.

This library provides a small messaging protocol for a CIoT device, intended for use with the Water Meter Demo. As well as building for the C027 target, files are included for building a C DLL and, from that, a C Sharp DLL which can be linked into the PC-end of the Water Meter Demo (see the .ZIP file stored in the Wiki of the Water Meter Demo project) to provide end-to-end messaging with complete transparency. Since these PC files cannot be built inside mbed the source files are post-fixed with a ".txt" extension to keep them out of the way.

If a water pump is to be switched on/off as part of the demo, an interface circuit is required, which is described on the Wiki of the WaterMeterSupport library.

Files at this revision

API Documentation at this revision

Comitter:
RobMeades
Date:
Fri May 22 11:41:38 2015 +0000
Commit message:
Initial commit of CIoT message protocol, used for water meter demo demonstrations, to mbed cloud.

Changed in this revision

IotMeterApi.hpp Show annotated file Show diff for this revision Revisions of this file
IotMeterDllWrapper.cpp.txt Show annotated file Show diff for this revision Revisions of this file
IotMeterDllWrapper.cs.txt Show annotated file Show diff for this revision Revisions of this file
IotMeterDllWrapper.hpp.txt Show annotated file Show diff for this revision Revisions of this file
IotMeterMsgCodec.cpp Show annotated file Show diff for this revision Revisions of this file
IotMeterMsgs.hpp Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 5c46cb3be899 IotMeterApi.hpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IotMeterApi.hpp	Fri May 22 11:41:38 2015 +0000
@@ -0,0 +1,423 @@
+/* C027N/water-meter interface definition for Water Meter Demo
+ * 
+ * Copyright (C) u-blox Melbourn Ltd
+ * u-blox Melbourn Ltd, Melbourn, UK
+ * 
+ * All rights reserved.
+ *
+ * This source file is the sole property of u-blox Melbourn Ltd.
+ * Reproduction or utilization of this source in whole or part is
+ * forbidden without the written consent of u-blox Melbourn Ltd.
+ */
+
+#ifndef IOT_METER_API_HPP
+#define IOT_METER_API_HPP
+
+/**
+ * @file iot_meter_api.h
+ * This file defines the API to the C027N/water-meter device
+ * for the MWC demo 2015.
+ */
+
+#include <IotMeterMsgs.hpp>
+
+// ----------------------------------------------------------------
+// HARDWARE
+// The code in this library is setup to expect a Water Pump Relay,
+// a Crydom D2425, to be connected through this circuit:
+//
+// C027N board                     D2425 (Water Pump Relay)
+//
+//    5V   o------------o 
+//                      | 
+//                     | |
+//                     | |  180 Ohm
+//                     | |
+//                      |
+//                      o--------------o '+3' pin
+//            10k Ohm   |
+//              ___    / 
+//     D7   o--|___|--| (transistor)
+//                    _\/
+//                      |
+//                      |
+//                      |
+//    Gnd   o-----------o--------------o '4' pin
+//                      
+// ----------------------------------------------------------------
+
+// ----------------------------------------------------------------
+// GENERAL COMPILE-TIME CONSTANTS
+// ----------------------------------------------------------------
+
+/// The maximum length of a raw datagram in bytes
+#define MAX_DATAGRAM_SIZE_RAW 20
+
+// The GPIO that the water pump is connected to on the C027N board
+#define GPIO_WATER_PUMP 7
+
+// ----------------------------------------------------------------
+// CLASSES
+// ----------------------------------------------------------------
+
+class MessageCodec {
+public:
+
+    // ----------------------------------------------------------------
+    // MESSAGE ENCODING FUNCTIONS
+    // ----------------------------------------------------------------
+
+    /// Encode an uplink message that is sent at power-on of the
+    // device.  Indicates that the device has been initialised.  After
+    // transmission of this message meter readings will be returned
+    // at the indicated rate.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeInitIndUlMsg (char * pBuffer,
+                                 InitIndUlMsg_t * pMsg);
+
+    /// Encode a downlink message that reboots the device.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeRebootReqDlMsg (char * pBuffer,
+                                   RebootReqDlMsg_t *pMsg);
+
+    /// Encode an uplink message containing the current meter reading.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeVolumeIndUlMsg (char * pBuffer,
+                                   VolumeIndUlMsg_t * pMsg);
+
+    /// Encode an uplink message containing the current RSSI reading.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeRssiIndUlMsg (char * pBuffer,
+                                 RssiIndUlMsg_t * pMsg);
+
+    /// Encode a downlink message that sets the reading interval
+    // of the water meter.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \return  The number of bytes encoded.
+    uint32_t encodeSerialNumberGetReqDlMsg (char * pBuffer);
+
+    /// Encode an uplink message that is sent as a response to a
+    // SerialNumberGetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeSerialNumberGetCnfUlMsg (char * pBuffer,
+                                            SerialNumberGetCnfUlMsg_t * pMsg);
+
+    /// Encode an uplink message that is sent at power-on of the
+    // device and in response to an InitReqDlMsg.  Indicates the
+    // serial number of the device.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeSerialNumberIndUlMsg (char * pBuffer,
+                                         SerialNumberIndUlMsg_t * pMsg);
+
+    /// Encode a downlink message that retrieves the reading interval.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeReadingIntervalGetReqDlMsg (char * pBuffer);
+
+    /// Encode an uplink message that is sent as a response to a
+    // ReadingIntervalGetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeReadingIntervalGetCnfUlMsg (char * pBuffer,
+                                               ReadingIntervalGetCnfUlMsg_t * pMsg);
+
+    /// Encode a downlink message that sets the reading interval.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \return  The number of bytes encoded.
+    uint32_t encodeReadingIntervalSetReqDlMsg (char * pBuffer,
+                                               ReadingIntervalSetReqDlMsg_t * pMsg);
+
+    /// Encode an uplink message that is sent as a response to a
+    // ReadingIntervalSetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeReadingIntervalSetCnfUlMsg (char * pBuffer,
+                                               ReadingIntervalSetCnfUlMsg_t * pMsg);
+
+    /// Encode a downlink message that gets the state of a GPIO
+    // on the C027N board.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \return  The number of bytes encoded.
+    uint32_t encodeGpioGetReqDlMsg (char * pBuffer,
+                                    GpioGetReqDlMsg_t *pMsg);
+
+    /// Encode an uplink message that is sent as a response to a
+    // GpioGetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeGpioGetCnfUlMsg (char * pBuffer,
+                                    GpioGetCnfUlMsg_t * pMsg);
+
+    /// Encode a downlink message that sets the state of a GPIO
+    // on the C027N board.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeGpioSetReqDlMsg (char * pBuffer,
+                                    GpioSetReqDlMsg_t * pMsg);
+
+    /// Encode an uplink message that is sent as a response to a
+    // GpioSetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeGpioSetCnfUlMsg (char * pBuffer,
+                                    GpioSetCnfUlMsg_t * pMsg);
+
+    /// Encode a downlink message that gets the steady state of the
+    // red LED on the C027N board.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \return  The number of bytes encoded.
+    uint32_t encodeLedGetReqDlMsg (char * pBuffer);
+
+    /// Encode an uplink message that is sent as a response to an
+    // LedGetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeLedGetCnfUlMsg (char * pBuffer,
+                                   LedGetCnfUlMsg_t * pMsg);
+
+    /// Encode a downlink message that sets the steady state of
+    // the red LED.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeLedSetReqDlMsg (char * pBuffer,
+                                   LedSetReqDlMsg_t * pMsg);
+
+    /// Encode an uplink message that is sent as a response to an
+    // LedSetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeLedSetCnfUlMsg (char * pBuffer,
+                                   LedSetCnfUlMsg_t * pMsg);
+
+    /// Encode a downlink message that sets the red LED to flash when
+    // a message is being sent/received (or not).  If LedSetReqDlMsg
+    // has set the LED to ON the flash will be 'inverted', i.e. the
+    // red LED will blink off when a message is being transmitted
+    // or received instead.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeFlashSetReqDlMsg (char * pBuffer,
+                                     FlashSetReqDlMsg_t * pMsg);
+
+    /// Encode an uplink message that is sent as a response to a
+    // FlashSetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeFlashSetCnfUlMsg (char * pBuffer,
+                                     FlashSetCnfUlMsg_t * pMsg);
+
+    /// Encode a downlink message that gets the red LED to flash when
+    // a message is being sent/received (or not).  If LedSetReqDlMsg
+    // has set the LED to ON the flash will be 'inverted', i.e. the
+    // red LED will blink off when a message is being transmitted
+    // or received instead.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeFlashGetReqDlMsg (char * pBuffer);
+
+    /// Encode an uplink message that is sent as a response to a
+    // FlashGetReqDlMsg.
+    // \param pBuffer  A pointer to the buffer to encode into.  The
+    // buffer length must be at least MAX_MESSAGE_SIZE long
+    // \param pMsg  A pointer to the message to send.
+    // \return  The number of bytes encoded.
+    uint32_t encodeFlashGetCnfUlMsg (char * pBuffer,
+                                     FlashGetCnfUlMsg_t * pMsg);
+
+    // ----------------------------------------------------------------
+    // MESSAGE DECODING FUNCTIONS
+    // ----------------------------------------------------------------
+
+    /// The outcome of message decoding.
+    //
+    // !!! When you add anything to the generic of UL sections
+    // here (but not the DL bits as C# never decodes a DL thing),
+    // align it with the DLL exported version in the dll wrapper files
+    // so that the C# application can decode it.
+    typedef enum DecodeResultTag_t
+    {
+      DECODE_RESULT_FAILURE = 0,         //!< Generic failed decode.
+      DECODE_RESULT_INPUT_TOO_SHORT,     //!< Not enough input bytes.
+      DECODE_RESULT_OUTPUT_TOO_SHORT,    //!< Not enough room in the
+                                         //! output.
+      DECODE_RESULT_UNKNOWN_MSG_ID,      //!< Rogue message ID.
+      DECODE_RESULT_DL_MSG_BASE = 0x40,  //!< From here on are the
+                                         //! downlink messages.
+                                         // !!! If you add one here
+                                         // update the next line !!!
+      DECODE_RESULT_REBOOT_REQ_DL_MSG = DECODE_RESULT_DL_MSG_BASE,
+      DECODE_RESULT_SERIAL_NUMBER_GET_REQ_DL_MSG,
+      DECODE_RESULT_READING_INTERVAL_SET_REQ_DL_MSG,
+      DECODE_RESULT_READING_INTERVAL_GET_REQ_DL_MSG,
+      DECODE_RESULT_GPIO_SET_REQ_DL_MSG,
+      DECODE_RESULT_GPIO_GET_REQ_DL_MSG,
+      DECODE_RESULT_LED_SET_REQ_DL_MSG,
+      DECODE_RESULT_LED_GET_REQ_DL_MSG,
+      DECODE_RESULT_FLASH_SET_REQ_DL_MSG,
+      DECODE_RESULT_FLASH_GET_REQ_DL_MSG,  // !!! If you add one here
+                                           // update the next line !!!
+      MAX_DL_REQ_MSG = DECODE_RESULT_FLASH_GET_REQ_DL_MSG,
+      DECODE_RESULT_UL_MSG_BASE = 0x80, //!< From here on are the
+                                        //! uplink messages.
+      DECODE_RESULT_INIT_IND_UL_MSG = DECODE_RESULT_UL_MSG_BASE,
+      DECODE_RESULT_SERIAL_NUMBER_IND_UL_MSG,
+      DECODE_RESULT_SERIAL_NUMBER_GET_CNF_UL_MSG,
+      DECODE_RESULT_VOLUME_IND_UL_MSG,
+      DECODE_RESULT_RSSI_IND_UL_MSG,
+      DECODE_RESULT_READING_INTERVAL_SET_CNF_UL_MSG,
+      DECODE_RESULT_READING_INTERVAL_GET_CNF_UL_MSG,
+      DECODE_RESULT_GPIO_SET_CNF_UL_MSG,
+      DECODE_RESULT_GPIO_GET_CNF_UL_MSG,
+      DECODE_RESULT_LED_SET_CNF_UL_MSG,
+      DECODE_RESULT_LED_GET_CNF_UL_MSG,
+      DECODE_RESULT_FLASH_SET_CNF_UL_MSG,
+      DECODE_RESULT_FLASH_GET_CNF_UL_MSG,  // !!! If you add one here update
+                                             // the next line !!!
+      MAX_UL_REQ_MSG = DECODE_RESULT_FLASH_GET_CNF_UL_MSG,
+      MAX_NUM_DECODE_RESULTS             //!< The maximum number of
+                                         //! decode results.
+    } DecodeResult_t;
+
+    /// Decode a downlink message. When a datagram has been received
+    // this function should be called iteratively to decode all the
+    // messages contained within it.  The result, in pOutputBuffer,
+    // should be cast by the calling function to DlMsgUnion_t and
+    // the relevant member selected according to the
+    // DecodeResult_t code.
+    // \param ppInBuffer  A pointer to the pointer to decode from.
+    // On completion this is pointing to the next byte that
+    // could be decoded, after the currently decoded message,
+    // in the buffer.
+    // \param sizeInBuffer  The number of bytes left to decode.
+    // \param pOutBuffer  A pointer to the buffer to write the
+    // result into.
+    // \param pBytesDecoded A pointer to a place to write the number
+    // of bytes decoded.
+    // \return  The result of the decoding, which hopefully says
+    // what message has been decoded.
+    DecodeResult_t decodeDlMsg (const char ** ppInBuffer,
+                                uint32_t sizeInBuffer,
+                                DlMsgUnion_t * pOutBuffer);
+
+    /// Decode an uplink message. When a datagram has been received
+    // this function should be called iteratively to decode all the
+    // messages contained within it.  The result, in pOutputBuffer,
+    // should be cast by the calling function to UlMsgUnion_t and
+    // the relevant member selected according to the
+    // DecodeResult_t code.
+    // \param ppInBuffer  A pointer to the pointer to decode from.
+    // On completion this is pointing to the next byte that
+    // could be decoded, after the currently decoded message,
+    // in the buffer.
+    // \param sizeInBuffer  The number of bytes left to decode.
+    // \param pOutBuffer  A pointer to the buffer to write the
+    // result into.
+    // \return  The result of the decoding, which hopefully says
+    // what message has been decoded.
+    DecodeResult_t decodeUlMsg (const char ** ppInBuffer,
+                                uint32_t sizeInBuffer,
+                                UlMsgUnion_t * pOutBuffer);
+
+    // ----------------------------------------------------------------
+    // MISC FUNCTIONS
+    // ----------------------------------------------------------------
+
+    /// Only used in the DLL form, sets up the "printf()" function
+    // for logging.
+    // \param guiPrintToConsole  the printf function.
+    void initDll (void (*guiPrintToConsole) (const char *));
+
+    /// User callback function for "printf()" logging.  
+    static void (*mp_guiPrintToConsole) (const char *);
+
+private:
+    /// Encode a boolean value.
+    // \param pBuffer  A pointer to where the encoded
+    // value should be placed.
+    // \param value The Boolean value.
+    // \return  The number of bytes encoded.
+    uint32_t encodeBool (char * pBuffer, bool value);
+    /// Decode a Boolean value.
+    // \param ppBuffer  A pointer to the pointer to decode.
+    // On completion this points to the location after the
+    // bool in the input buffer.
+    // \return  The decoded value.
+    bool decodeBool (const char ** ppBuffer);
+    /// Encode a uint32_t value.
+    // \param pBuffer  A pointer to the value to decode.
+    // \param value The value.
+    // \return  The number of bytes encoded.
+    uint32_t encodeUint32 (char * pBuffer, uint32_t value);
+    /// Decode a uint32_t value.
+    // \param ppBuffer  A pointer to the pointer to decode.
+    // On completion this points to the location after the
+    // uint32 in the input buffer.
+    uint32_t decodeUint32 (const char ** ppBuffer);
+    /// Encode GpioState.
+    // \param pBuffer  A pointer to the value to decode.
+    // \param gGpioState A pointer to the place to put the
+    // GPIO Sate.
+    // \return  The number of bytes encoded.
+    uint32_t encodeGpioState (char * pBuffer, GpioState_t *pGpioState);
+    /// Decode Gpio_State_t.
+    // \param gGpioState_t a pointer to a place to put the
+    // decoded GPIO state.
+    // \param ppBuffer  A pointer to the pointer to decode.
+    // On completion this points to the location after the
+    // uint32 in the input buffer.
+    void decodeGpioState (GpioState_t * pGpioState,
+                          const char ** ppBuffer);
+    /// Log a message for debugging, "printf()" style.
+    // \param pFormat The printf() stle parameters.
+    void logMsg (const char * pFormat, ...);
+};
+
+#endif
+
+// End Of File
diff -r 000000000000 -r 5c46cb3be899 IotMeterDllWrapper.cpp.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IotMeterDllWrapper.cpp.txt	Fri May 22 11:41:38 2015 +0000
@@ -0,0 +1,234 @@
+/* DLL wrapper for C027N/water-meter message handling functions
+ * for MWC demo 2015
+ * 
+ * Copyright (C) u-blox Melbourn Ltd
+ * u-blox Melbourn Ltd, Melbourn, UK
+ * 
+ * All rights reserved.
+ *
+ * This source file is the sole property of u-blox Melbourn Ltd.
+ * Reproduction or utilization of this source in whole or part is
+ * forbidden without the written consent of u-blox Melbourn Ltd.
+ */
+
+/**
+ * @file iot_meter_dll_wrapper.cpp
+ * This file implements the encapsulation necessary to
+ * call the CPP Message Handling functions from C#.
+ */
+#include <stdint.h>
+#include <IotMeterApi.hpp>
+#include <IotMeterDllWrapper.hpp>
+#include "windows.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    // Instantiate a messageHandler 
+    MessageCodec gMessageCodec;
+
+    // ----------------------------------------------------------------
+    // CONSTANTS
+    // ----------------------------------------------------------------
+
+    uint32_t __cdecl maxDatagramSizeRaw (void)
+    {
+        return MAX_DATAGRAM_SIZE_RAW;
+    }
+    
+    uint32_t __cdecl gpioWaterPump (void)
+    {
+        return GPIO_WATER_PUMP;
+    }
+    
+    // ----------------------------------------------------------------
+    // PRIVATE FUNCTIONS
+    // ----------------------------------------------------------------
+
+    // ----------------------------------------------------------------
+    // MESSAGE ENCODE WRAPPER FUNCTIONS
+    // ----------------------------------------------------------------
+
+    // Wrap encodeRebootReqDlMsg 
+    uint32_t __cdecl encodeRebootReqDlMsg (char * pBuffer,
+                                           bool devModeOnNotOff)
+    {
+        RebootReqDlMsg_t msg;
+        msg.devModeOnNotOff = devModeOnNotOff;
+
+        return gMessageCodec.encodeRebootReqDlMsg (pBuffer, &msg);
+    }
+
+    // Wrap encodeSerialNumberGetReqDlMsg 
+    uint32_t __cdecl encodeSerialNumberGetReqDlMsg (char * pBuffer)
+    {
+        return gMessageCodec.encodeSerialNumberGetReqDlMsg (pBuffer);
+    }
+
+    // Wrap encodeReadingIntervalSetReqDlMsg 
+    uint32_t __cdecl encodeReadingIntervalSetReqDlMsg (char * pBuffer,
+                                                       uint32_t readingIntervalSeconds)
+    {
+        ReadingIntervalSetReqDlMsg_t msg;
+        msg.readingIntervalSeconds = readingIntervalSeconds;
+
+        return gMessageCodec.encodeReadingIntervalSetReqDlMsg (pBuffer, &msg);
+    }
+
+    // Wrap encodeReadingIntervalGetReqDlMsg 
+    uint32_t __cdecl encodeReadingIntervalGetReqDlMsg (char * pBuffer)
+    {
+        return gMessageCodec.encodeReadingIntervalGetReqDlMsg (pBuffer);
+    }
+
+    // Wrap encodeGpioSetReqDlMsg
+    uint32_t __cdecl encodeGpioSetReqDlMsg (char * pBuffer,
+                                            uint8_t gpio,
+                                            bool inputNotOutput,
+                                            bool onNotOff)
+    {
+        GpioSetReqDlMsg_t msg;
+        msg.gpioState.gpio = gpio;
+        msg.gpioState.inputNotOutput = inputNotOutput;
+        msg.gpioState.onNotOff = onNotOff;
+
+        return gMessageCodec.encodeGpioSetReqDlMsg (pBuffer, &msg);
+    }
+
+    // Wrap encodeGpioGetReqDlMsg
+    uint32_t __cdecl encodeGpioGetReqDlMsg (char * pBuffer,
+                                            uint32_t gpio)
+    {
+        GpioGetReqDlMsg_t msg;
+        msg.gpio = (uint8_t) gpio;
+
+        return gMessageCodec.encodeGpioGetReqDlMsg (pBuffer, &msg);
+    }
+
+    // Wrap encodeLedSetReqDlMsg 
+    uint32_t __cdecl encodeLedSetReqDlMsg (char * pBuffer,
+                                           bool onNotOff)
+    {
+        LedSetReqDlMsg_t msg;
+        msg.onNotOff = onNotOff;
+
+        return gMessageCodec.encodeLedSetReqDlMsg (pBuffer, &msg);
+    }
+
+    // Wrap encodeLedGetReqDlMsg 
+    uint32_t __cdecl encodeLedGetReqDlMsg (char * pBuffer)
+    {
+        return gMessageCodec.encodeLedGetReqDlMsg (pBuffer);
+    }
+
+    // Wrap encodeFlashSetReqDlMsg 
+    uint32_t __cdecl encodeFlashSetReqDlMsg (char * pBuffer,
+                                             bool onNotOff)
+    {
+        FlashSetReqDlMsg_t msg;
+        msg.onNotOff = onNotOff;
+
+        return gMessageCodec.encodeFlashSetReqDlMsg (pBuffer, &msg);
+    }
+
+    // Wrap encodeFlashGetReqDlMsg 
+    uint32_t __cdecl encodeFlashGetReqDlMsg (char * pBuffer)
+    {
+        return gMessageCodec.encodeFlashGetReqDlMsg (pBuffer);
+    }
+
+    // ----------------------------------------------------------------
+    // MESSAGE DECODE WRAPPER FUNCTIONS
+    // ----------------------------------------------------------------
+
+    // Unpack a gpioState struct from with a uint32_t value
+    void __cdecl unpackGpioState (uint32_t value,
+                                  uint32_t *pGpio,
+                                  bool * pInputNotOutput,
+                                  bool * pOnNotOff)
+    {
+        *pGpio = (uint32_t) ((value & 0x00FF0000) >> 16);
+        if (pInputNotOutput != NULL)
+        {
+            *pInputNotOutput = (bool) ((value & 0x0000FF00) >> 8);
+        }
+        if (pOnNotOff != NULL)
+        {
+            *pOnNotOff = (bool) (value & 0x000000FF);
+        }
+    }
+
+    // Wrap decodeUlMsg 
+    uint32_t __cdecl decodeUlMsg (const char ** ppInBuffer,
+                                  uint32_t sizeInBuffer,
+                                  uint32_t * pContents)
+    {
+        MessageCodec::DecodeResult_t decodeResult;
+        UlMsgUnion_t outBuffer;
+        decodeResult = gMessageCodec.decodeUlMsg (ppInBuffer,
+                                                    sizeInBuffer,
+                                                    &outBuffer);
+        switch (decodeResult)
+        {
+            case (MessageCodec::DECODE_RESULT_INIT_IND_UL_MSG):
+                *pContents = (uint32_t) outBuffer.initIndUlMsg.wakeUpCode;
+            break;
+            case (MessageCodec::DECODE_RESULT_SERIAL_NUMBER_GET_CNF_UL_MSG):
+                *pContents = outBuffer.serialNumberCnfUlMsg.serialNumber;
+            break;
+            case (MessageCodec::DECODE_RESULT_SERIAL_NUMBER_IND_UL_MSG):
+                *pContents = outBuffer.serialNumberIndUlMsg.serialNumber;
+            break;
+            case (MessageCodec::DECODE_RESULT_VOLUME_IND_UL_MSG):
+                *pContents = outBuffer.volumeIndUlMsg.volumeLitres;
+            break;
+            case (MessageCodec::DECODE_RESULT_RSSI_IND_UL_MSG):
+                *pContents = outBuffer.rssiIndUlMsg.rssi;
+            break;
+            case (MessageCodec::DECODE_RESULT_READING_INTERVAL_SET_CNF_UL_MSG):
+                *pContents = outBuffer.readingIntervalSetCnfUlMsg.readingIntervalSeconds;
+            break;
+            case (MessageCodec::DECODE_RESULT_READING_INTERVAL_GET_CNF_UL_MSG):
+                *pContents = outBuffer.readingIntervalGetCnfUlMsg.readingIntervalSeconds;
+            break;
+            case (MessageCodec::DECODE_RESULT_GPIO_SET_CNF_UL_MSG):
+                *pContents = (uint32_t) outBuffer.gpioSetCnfUlMsg;
+            break;
+            case (MessageCodec::DECODE_RESULT_GPIO_GET_CNF_UL_MSG):
+                *pContents = (uint32_t) outBuffer.gpioGetCnfUlMsg;
+            break;
+            case (MessageCodec::DECODE_RESULT_LED_SET_CNF_UL_MSG):
+                *pContents = (uint32_t) outBuffer.ledSetCnfUlMsg.onNotOff;
+            break;
+            case (MessageCodec::DECODE_RESULT_LED_GET_CNF_UL_MSG):
+                *pContents = (uint32_t) outBuffer.ledGetCnfUlMsg.onNotOff;
+            break;
+            case (MessageCodec::DECODE_RESULT_FLASH_SET_CNF_UL_MSG):
+                *pContents = (uint32_t) outBuffer.flashSetCnfUlMsg.onNotOff;
+            break;
+            case (MessageCodec::DECODE_RESULT_FLASH_GET_CNF_UL_MSG):
+                *pContents = (uint32_t) outBuffer.flashGetCnfUlMsg.onNotOff;
+            break;
+            default:
+            // The decodeResult will be left as Unknown message
+            break;
+        }
+
+        return decodeResult;
+    }
+
+    // ----------------------------------------------------------------
+    // MISC FUNCTIONS
+    // ----------------------------------------------------------------
+
+    void  initDll (void (*printToConsole) (const char *))
+    {
+        gMessageCodec.initDll (printToConsole);
+    } 
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+// End Of File
diff -r 000000000000 -r 5c46cb3be899 IotMeterDllWrapper.cs.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IotMeterDllWrapper.cs.txt	Fri May 22 11:41:38 2015 +0000
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Microsoft.Win32.SafeHandles;
+using System.Threading;
+
+namespace MessageCodec
+{
+    /// <summary>
+    /// This is a wrapper around the native dll for MessageCodec calls
+    /// </summary>
+    public class MessageCodec_dll
+    {
+
+        [DllImport ("Kernel32.dll")]
+        extern static SafeFileHandle GetStdHandle(Int32 nStdHandle);
+
+        /// <summary>
+        /// Windows specific calls to load a library at runtime
+        /// </summary>
+
+        [DllImport ("kernel32.dll")]
+        internal static extern IntPtr LoadLibrary(String dllname);
+
+        /// <summary>
+        /// Windows specific call to get the address of a entry in a dll
+        /// </summary>
+
+        [DllImport ("kernel32.dll")]
+        internal static extern IntPtr GetProcAddress(IntPtr hModule, String procname);
+
+        /// The outcome of message decoding.
+        // !!! ORDER IS IMPORTANT AND THIS MUST align with the generic
+        // and UL (but not DL as the C# DLL is not intended to decode
+        // a DL message) portions of the C typedef DecodeResult_t.
+        public enum CSDecodeResult
+        {
+          DECODE_RESULT_FAILURE = 0,         //!< Generic failed decode.
+          DECODE_RESULT_INPUT_TOO_SHORT,     //!< Not enough input bytes.
+          DECODE_RESULT_OUTPUT_TOO_SHORT,    //!< Not enough room in the
+                                             //! output.
+          DECODE_RESULT_UNKNOWN_MSG_ID,      //!< Rogue message ID.
+          DECODE_RESULT_UL_MSG_BASE = 0x80,  //!< From here on are the
+                                             //! uplink messages.
+          DECODE_RESULT_INIT_IND_UL_MSG = DECODE_RESULT_UL_MSG_BASE,
+          DECODE_RESULT_SERIAL_NUMBER_IND_UL_MSG,
+          DECODE_RESULT_SERIAL_NUMBER_CNF_UL_MSG,
+          DECODE_RESULT_VOLUME_IND_UL_MSG,
+          DECODE_RESULT_RSSI_IND_UL_MSG,
+          DECODE_RESULT_READING_INTERVAL_SET_CNF_UL_MSG,
+          DECODE_RESULT_READING_INTERVAL_GET_CNF_UL_MSG,
+          DECODE_RESULT_GPIO_SET_CNF_UL_MSG,
+          DECODE_RESULT_GPIO_GET_CNF_UL_MSG,
+          DECODE_RESULT_LED_SET_CNF_UL_MSG,
+          DECODE_RESULT_LED_GET_CNF_UL_MSG,
+          DECODE_RESULT_FLASH_SET_CNF_UL_MSG,
+          DECODE_RESULT_FLASH_GET_CNF_UL_MSG,  // !!! If you add one here update
+                                               // the next line !!!
+          MAX_UL_REQ_MSG = DECODE_RESULT_FLASH_GET_CNF_UL_MSG,
+          MAX_NUM_DECODE_RESULTS             //!< The maximum number of
+                                             //! decode results.
+        };
+
+        // uint32_t __cdecl maxDatagramSizeRaw (void);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _maxDatagramSizeRaw ();
+        public _maxDatagramSizeRaw maxDatagramSizeRaw;
+
+        // uint32_t __cdecl gpioWaterPump (void);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _gpioWaterPump();
+        public _gpioWaterPump gpioWaterPump;
+
+        // uint32_t __cdecl encodeRebootReqDlMsg (char * pBuffer,
+        //                                        bool devModeOnNotOff);
+        [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeRebootReqDlMsg(byte* pBuffer,
+                                                            Boolean devModeOnNotOff);
+        public _encodeRebootReqDlMsg encodeRebootReqDlMsg;
+
+        // uint32_t __cdecl encodeSerialNumberGetReqDlMsg (char * pBuffer);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeSerialNumberGetReqDlMsg(byte* pBuffer);
+        public _encodeSerialNumberGetReqDlMsg encodeSerialNumberGetReqDlMsg;
+
+        // uint32_t __cdecl encodeReadingIntervalSetReqDlMsg (char * pBuffer,
+        //                                                     UInt32 readingIntervalSeconds);
+        [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeReadingIntervalSetReqDlMsg(byte* pBuffer,
+                                                                         UInt32 readingIntervalSeconds);
+        public _encodeReadingIntervalSetReqDlMsg encodeReadingIntervalSetReqDlMsg;
+
+        // uint32_t __cdecl encodeReadingIntervalGetReqDlMsg (char * pBuffer);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeReadingIntervalGetReqDlMsg(byte* pBuffer);
+        public _encodeReadingIntervalGetReqDlMsg encodeReadingIntervalGetReqDlMsg;
+
+        // uint32_t __cdecl encodeGpioSetReqDlMsg (char * pBuffer,
+        //                                         uint8_t gpio,
+        //                                         bool inputNotOutput,
+        //                                         bool onNotOff);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeGpioSetReqDlMsg(byte* pBuffer,
+                                                             UInt32 gpio,
+                                                             Boolean inputNotOutput,
+                                                             Boolean onNotOff);
+        public _encodeGpioSetReqDlMsg encodeGpioSetReqDlMsg;
+
+        // uint32_t __cdecl encodeGpioGetReqDlMsg (char * pBuffer,
+        //                                         uint8_t gpio);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeGpioGetReqDlMsg(byte* pBuffer,
+                                                             UInt32 gpio);
+        public _encodeGpioGetReqDlMsg encodeGpioGetReqDlMsg;
+
+        // uint32_t __cdecl encodeLedSetReqDlMsg (char * pBuffer,
+        //                                         bool onNotOff);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeLedSetReqDlMsg(byte* pBuffer,
+                                                            Boolean onNotOff);
+        public _encodeLedSetReqDlMsg encodeLedSetReqDlMsg;
+
+        // uint32_t __cdecl encodeLedGetReqDlMsg (char * pBuffer);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeLedGetReqDlMsg(byte* pBuffer);
+        public _encodeLedGetReqDlMsg encodeLedGetReqDlMsg;
+
+        // uint32_t __cdecl encodeFlashSetReqDlMsg (char * pBuffer,
+        //                                           bool onNotOff);
+        [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeFlashSetReqDlMsg(byte * pBuffer,
+                                                              Boolean onNotOff);
+        public _encodeFlashSetReqDlMsg encodeFlashSetReqDlMsg;
+
+        // uint32_t __cdecl encodeFlashGetReqDlMsg (char * pBuffer);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate UInt32 _encodeFlashGetReqDlMsg(byte* pBuffer);
+        public _encodeFlashGetReqDlMsg encodeFlashGetReqDlMsg;
+
+        // void __cdecl unpackGpioState (uint32_t value,
+        //                               uint32_t *pGpio,
+        //                               bool * pInputNotOutput,
+        //                               bool * pOnNotOff);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public unsafe delegate void _unpackGpioState(UInt32 value,
+                                                     UInt32* pGpio,
+                                                     Boolean *pInputNotOutput,
+                                                     Boolean *pOnNotOff);
+        public _unpackGpioState unpackGpioState;
+
+        // CsDecodeResult_t __cdecl decodeUlMsg (const char ** ppInBuffer,
+        //                                       uint32_t sizeInBuffer,
+        //                                       uint32_t * pContents);
+        [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+        public unsafe delegate CSDecodeResult _decodeUlMsg(byte** ppInBuffer,
+                                                           UInt32 sizeInBuffer,
+                                                           UInt32* pContents);
+        public _decodeUlMsg decodeUlMsg;
+
+        [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+        public delegate void guiPrintToConsoleCallback(StringBuilder data);
+
+        [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+        public delegate void _initDll([MarshalAs (UnmanagedType.FunctionPtr)] guiPrintToConsoleCallback callbackPointer);
+        public _initDll initDll;
+
+        void guiPrintToConsole(StringBuilder data)
+        {
+            if (onConsoleTrace != null)
+            {
+                onConsoleTrace (data.ToString ());
+            }
+        }
+
+        /// <summary>
+        /// Load the dll and do the bindings
+        /// </summary>
+        /// <param name="dllLocation">location of dll</param>
+        public void bindDll(string dllLocation)
+        {
+            IntPtr ptrDll = LoadLibrary (dllLocation);
+
+            if (ptrDll == IntPtr.Zero) throw new Exception (String.Format ("Cannot find {0}", dllLocation));
+
+            maxDatagramSizeRaw = (_maxDatagramSizeRaw)bindItem(ptrDll, "maxDatagramSizeRaw", typeof(_maxDatagramSizeRaw));
+            gpioWaterPump = (_gpioWaterPump)bindItem(ptrDll, "gpioWaterPump", typeof(_gpioWaterPump));
+            encodeRebootReqDlMsg = (_encodeRebootReqDlMsg)bindItem(ptrDll, "encodeRebootReqDlMsg", typeof(_encodeRebootReqDlMsg));
+            encodeSerialNumberGetReqDlMsg = (_encodeSerialNumberGetReqDlMsg)bindItem(ptrDll, "encodeSerialNumberGetReqDlMsg", typeof(_encodeSerialNumberGetReqDlMsg));
+            encodeReadingIntervalSetReqDlMsg = (_encodeReadingIntervalSetReqDlMsg)bindItem(ptrDll, "encodeReadingIntervalSetReqDlMsg", typeof(_encodeReadingIntervalSetReqDlMsg));
+            encodeReadingIntervalGetReqDlMsg = (_encodeReadingIntervalGetReqDlMsg)bindItem(ptrDll, "encodeReadingIntervalGetReqDlMsg", typeof(_encodeReadingIntervalGetReqDlMsg));
+            encodeGpioSetReqDlMsg = (_encodeGpioSetReqDlMsg)bindItem(ptrDll, "encodeGpioSetReqDlMsg", typeof(_encodeGpioSetReqDlMsg));
+            encodeGpioGetReqDlMsg = (_encodeGpioGetReqDlMsg)bindItem(ptrDll, "encodeGpioGetReqDlMsg", typeof(_encodeGpioGetReqDlMsg));
+            encodeLedSetReqDlMsg = (_encodeLedSetReqDlMsg)bindItem(ptrDll, "encodeLedSetReqDlMsg", typeof(_encodeLedSetReqDlMsg));
+            encodeLedGetReqDlMsg = (_encodeLedGetReqDlMsg)bindItem(ptrDll, "encodeLedGetReqDlMsg", typeof(_encodeLedGetReqDlMsg));
+            encodeFlashSetReqDlMsg = (_encodeFlashSetReqDlMsg)bindItem(ptrDll, "encodeFlashSetReqDlMsg", typeof(_encodeFlashSetReqDlMsg));
+            encodeFlashGetReqDlMsg = (_encodeFlashGetReqDlMsg)bindItem(ptrDll, "encodeFlashGetReqDlMsg", typeof(_encodeFlashGetReqDlMsg));
+            unpackGpioState = (_unpackGpioState)bindItem(ptrDll, "unpackGpioState", typeof(_unpackGpioState));
+            decodeUlMsg = (_decodeUlMsg)bindItem(ptrDll, "decodeUlMsg", typeof(_decodeUlMsg));
+            initDll = (_initDll)bindItem (ptrDll, "initDll", typeof (_initDll));
+            initDll (guiPrintToConsole);
+        }
+
+        public object bindItem(IntPtr ptrDll, string dllFuncName, Type type)
+        {
+            // Get pointer to dllexport function
+            IntPtr procaddr = GetProcAddress (ptrDll, dllFuncName);
+            if (procaddr == IntPtr.Zero) throw new Exception (String.Format ("Cannot find {0}", dllFuncName));
+
+            // Bind it to the function
+            Object result = Marshal.GetDelegateForFunctionPointer (procaddr, type);
+            if (result == null) throw new Exception (String.Format ("Cannot bind to {0}", dllFuncName));
+
+            return result;
+        }
+
+        public delegate void ConsoleTrace(string data);
+        public event ConsoleTrace onConsoleTrace;
+    }
+}
\ No newline at end of file
diff -r 000000000000 -r 5c46cb3be899 IotMeterDllWrapper.hpp.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IotMeterDllWrapper.hpp.txt	Fri May 22 11:41:38 2015 +0000
@@ -0,0 +1,66 @@
+/* DLL wrapper for C027N/water-meter message handling functions
+ * for MWC demo 2015
+ * 
+ * Copyright (C) u-blox Melbourn Ltd
+ * u-blox Melbourn Ltd, Melbourn, UK
+ * 
+ * All rights reserved.
+ *
+ * This source file is the sole property of u-blox Melbourn Ltd.
+ * Reproduction or utilization of this source in whole or part is
+ * forbidden without the written consent of u-blox Melbourn Ltd.
+ */
+
+#ifndef IOT_METER_DLL_WRAPPER_HPP
+#define IOT_METER_DLL_WRAPPER_HPP
+
+/**
+ * @file iot_meter_dll_wrapper.h
+ * This file defines the encapsulation necessary to
+ * call the CPP Message Handling functions from C#.
+ */
+ 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    #define DLL __declspec(dllexport)
+
+    DLL uint32_t __cdecl maxDatagramSizeRaw (void);
+    DLL uint32_t __cdecl gpioWaterPump (void);
+
+    DLL uint32_t __cdecl encodeRebootReqDlMsg (char * pBuffer,
+                                               bool devModeOnNotOff);
+    DLL uint32_t __cdecl encodeSerialNumberGetReqDlMsg (char * pBuffer);
+    DLL uint32_t __cdecl encodeReadingIntervalSetReqDlMsg (char * pBuffer,
+                                                            uint32_t readingIntervalSeconds);
+    DLL uint32_t __cdecl encodeReadingIntervalGetReqDlMsg (char * pBuffer);
+    DLL uint32_t __cdecl encodeGpioSetReqDlMsg (char * pBuffer,
+                                                uint8_t gpio,
+                                                bool inputNotOutput,
+                                                bool onNotOff);
+    DLL uint32_t __cdecl encodeGpioGetReqDlMsg (char * pBuffer,
+                                                 uint32_t gpio);
+    DLL uint32_t __cdecl encodeLedSetReqDlMsg (char * pBuffer,
+                                                bool onNotOff);
+    DLL uint32_t __cdecl encodeLedGetReqDlMsg (char * pBuffer);
+    DLL uint32_t __cdecl encodeFlashSetReqDlMsg (char * pBuffer,
+                                                  bool onNotOff);
+    DLL uint32_t __cdecl encodeFlashGetReqDlMsg (char * pBuffer);
+    DLL void __cdecl unpackGpioState (uint32_t value,
+                                      uint32_t *pGpio,
+                                      bool * pInputNotOutput,
+                                      bool * pOnNotOff);
+    DLL uint32_t __cdecl decodeUlMsg (const char ** ppInBuffer,
+                                      uint32_t sizeInBuffer,
+                                      uint32_t * pContents);
+
+    DLL void  initDll (void (*guiPrintToConsole) (const char *)); 
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+// End Of File
diff -r 000000000000 -r 5c46cb3be899 IotMeterMsgCodec.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IotMeterMsgCodec.cpp	Fri May 22 11:41:38 2015 +0000
@@ -0,0 +1,728 @@
+/* C027N/water-meter interface for Water Meter Demo
+ * 
+ * Copyright (C) u-blox Melbourn Ltd
+ * u-blox Melbourn Ltd, Melbourn, UK
+ * 
+ * All rights reserved.
+ *
+ * This source file is the sole property of u-blox Melbourn Ltd.
+ * Reproduction or utilization of this source in whole or part is
+ * forbidden without the written consent of u-blox Melbourn Ltd.
+ */
+
+/**
+ * @file iot_meter_msg_handler.cpp
+ * This file implements the API to the C027N/water-meter device
+ * for the MWC demo 2015.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdarg.h> // for va_...
+#include <string.h> // for memcpy()
+#include <IotMeterApi.hpp>
+
+#ifdef DEBUG
+#define MESSAGE_CODEC_LOGMSG(...)    MessageCodec::logMsg(__VA_ARGS__)
+#else
+#define MESSAGE_CODEC_LOGMSG(...)
+#endif
+
+// ----------------------------------------------------------------
+// GENERAL COMPILE-TIME CONSTANTS
+// ----------------------------------------------------------------
+
+/// The max size of a debug message (including terminator)
+#define MAX_DEBUG_MESSAGE_LEN 128
+
+// ----------------------------------------------------------------
+// PRIVATE VARIABLES
+// ----------------------------------------------------------------
+
+void (*MessageCodec::mp_guiPrintToConsole) (const char*) = NULL;
+
+// ----------------------------------------------------------------
+// ON-AIR MESSAGE IDs
+// ----------------------------------------------------------------
+
+/// The message IDs in the downlink direction (i.e. to the
+// C027/water-meter device).  Note that 'over the air' these
+// are cast into an uint8_t.
+typedef enum MsgIdDlTag_t
+{
+  REBOOT_REQ_DL_MSG,            //!< Reboot the C027N/water-meter
+                                //! device.
+  SERIAL_NUMBER_GET_REQ_DL_MSG, //!< Get the serial number of the
+                                //! water-meter.
+  READING_INTERVAL_SET_REQ_DL_MSG, //!< Set the rate at which readings
+                                //! are returned by the C027N/water-meter
+                                //! device.
+  READING_INTERVAL_GET_REQ_DL_MSG, //!< Get the rate at which readings
+                                //! are returned by the C027N/water-meter
+                                //! device.
+  GPIO_SET_REQ_DL_MSG,          //!< Set the state of a GPIO on the
+                                //! C027N board.
+  GPIO_GET_REQ_DL_MSG,          //!< Get the state of a GPIO on the
+                                //! C027N board.
+  LED_SET_REQ_DL_MSG,           //!< Set the steady state of the red
+                                //! LED on the C027 board.
+  LED_GET_REQ_DL_MSG,           //!< Get the steady state of the red
+                                //! LED on the C027 board.
+  FLASH_SET_REQ_DL_MSG,         //!< Set the LED on the C027/water-meter
+                                //! device to flash when a reading is
+                                //! being sent (or not).
+  FLASH_GET_REQ_DL_MSG,         //!< Get whether the LED on the C027/
+                                //! flashes when a mesage is
+                                //! being sent/received (or not).
+  MAX_NUM_DL_MSGS               //!< The maximum number of downlink
+                                //! messages.
+} MsgIdDl_t;
+
+/// The message IDs in the uplink direction (i.e. from the
+// C027N/water-meter device).  Note that 'over the air' these are
+// cast into an uint8_t.
+typedef enum MsgIdUlTag_t
+{
+  INIT_IND_UL_MSG,              //!< Power on of the C027N/water meter
+                                //! device has completed.
+  SERIAL_NUMBER_IND_UL_MSG,     //!< The serial number of the water meter.
+  VOLUME_IND_UL_MSG,            //!< The current water meter reading.
+  RSSI_IND_UL_MSG,              //!< The current RSSI reading from the
+                                //! radio module.
+  SERIAL_NUMBER_GET_CNF_UL_MSG,     //!< The serial number of the water-meter.
+  READING_INTERVAL_SET_CNF_UL_MSG, //!< The rate at which readings are
+                                //! returned by the C027/water-meter device.
+  READING_INTERVAL_GET_CNF_UL_MSG, //!< The rate at which readings are
+                                //! returned by the C027/water-meter device.
+  GPIO_SET_CNF_UL_MSG,          //!< The state of a GPIO on the C027 board.
+  GPIO_GET_CNF_UL_MSG,          //!< The state of a GPIO on the C027 board.
+  LED_SET_CNF_UL_MSG,           //!< The steady state of the red LED on
+                                //! the C027/water-meter device.
+  LED_GET_CNF_UL_MSG,           //!< The steady state of the red LED on
+                                //! the C027/water-meter device.
+  FLASH_SET_CNF_UL_MSG,         //!< Whether the LED on the C027/water
+                                //!  meter device is set to flash when a
+                                //!  message is being sent/received (or not).
+  FLASH_GET_CNF_UL_MSG,         //!< Whether the LED on the C027/water
+                                //!  meter device is set to flash when a
+                                //!  message is being sent/received (or not).
+  MAX_NUM_UL_MSGS               //!< The maximum number of uplink messages.
+} MsgIdUl_t;
+
+// ----------------------------------------------------------------
+// GENERIC PRIVATE FUNCTIONS
+// ----------------------------------------------------------------
+
+/// Encode a boolean value, noting that this is contained
+// in a 32 bytes coded value.
+uint32_t MessageCodec::encodeBool (char * pBuffer, bool value)
+{
+    uint32_t numBytesEncoded = 4;
+
+    memset (pBuffer, 0, 4);
+    pBuffer[3] = value;
+
+    return numBytesEncoded;
+}
+
+/// Decode a boolean value, noting that this is contained
+// in a 32 bytes coded value.
+bool MessageCodec::decodeBool (const char ** ppBuffer)
+{
+    uint32_t value = 0;
+    bool boolValue = false;
+
+    value += ((**ppBuffer) & 0xFF) << 24;
+    (*ppBuffer)++;
+    value += ((**ppBuffer) & 0xFF) << 16;
+    (*ppBuffer)++;
+    value += ((**ppBuffer) & 0xFF) << 8;
+    (*ppBuffer)++;
+    value += ((**ppBuffer) & 0xFF) ;
+    (*ppBuffer)++;
+
+    if (value)
+    {
+        boolValue = true;
+    }
+
+    return boolValue;
+}
+
+/// Encode an int32
+uint32_t MessageCodec::encodeUint32 (char * pBuffer, uint32_t value)
+{
+    uint32_t numBytesEncoded = 4;
+
+    pBuffer[0] = 0xff & (value >> 24);
+    pBuffer[1] = 0xff & (value >> 16);
+    pBuffer[2] = 0xff & (value >> 8);
+    pBuffer[3] = 0xff & value;
+
+    return numBytesEncoded;
+}
+
+/// Decode an int32
+uint32_t MessageCodec::decodeUint32 (const char ** ppBuffer)
+{
+    uint32_t value = 0;
+
+    value += ((**ppBuffer) & 0xFF) << 24;
+    (*ppBuffer)++;
+    value += ((**ppBuffer) & 0xFF) << 16;
+    (*ppBuffer)++;
+    value += ((**ppBuffer) & 0xFF) << 8;
+    (*ppBuffer)++;
+    value += ((**ppBuffer) & 0xFF);
+    (*ppBuffer)++;
+
+    return value;
+}
+
+/// Encode GpioState
+uint32_t MessageCodec::encodeGpioState (char * pBuffer, GpioState_t *pGpioState)
+
+{
+    uint32_t numBytesEncoded = 0;
+
+    pBuffer[numBytesEncoded] = 0;
+    numBytesEncoded++;
+    pBuffer[numBytesEncoded] = pGpioState->gpio;
+    numBytesEncoded++;
+    pBuffer[numBytesEncoded] = pGpioState->inputNotOutput;
+    numBytesEncoded++;
+    pBuffer[numBytesEncoded] = pGpioState->onNotOff;
+    numBytesEncoded++;
+
+    return numBytesEncoded;
+}
+
+
+// Decode GpioState
+void MessageCodec::decodeGpioState (GpioState_t * pGpioState, const char ** ppBuffer)
+{
+    // Ignore the first byte as it's empty
+    (*ppBuffer)++;
+    pGpioState->gpio = (uint8_t) ((**ppBuffer) & 0xFF);
+    (*ppBuffer)++;
+    pGpioState->inputNotOutput = (bool) ((**ppBuffer) & 0xFF);
+    (*ppBuffer)++;
+    pGpioState->onNotOff = (bool) ((**ppBuffer) & 0xFF);
+    (*ppBuffer)++;
+}
+
+// ----------------------------------------------------------------
+// MESSAGE ENCODING FUNCTIONS
+// ----------------------------------------------------------------
+
+uint32_t MessageCodec::encodeInitIndUlMsg (char * pBuffer,
+                                             InitIndUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding InitIndUlMsg, ID 0x%.2x, ", INIT_IND_UL_MSG);
+    pBuffer[numBytesEncoded] = INIT_IND_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), (uint32_t) pMsg->wakeUpCode);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeRebootReqDlMsg (char * pBuffer,
+                                               RebootReqDlMsg_t *pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    pBuffer[numBytesEncoded] = REBOOT_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), pMsg->devModeOnNotOff);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeSerialNumberIndUlMsg (char * pBuffer,
+                                                     SerialNumberIndUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding SerialNumberIndUlMsg, ID 0x%.2x, ", SERIAL_NUMBER_IND_UL_MSG);
+    pBuffer[numBytesEncoded] = SERIAL_NUMBER_IND_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), pMsg->serialNumber);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeVolumeIndUlMsg (char * pBuffer,
+                                               VolumeIndUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding VolumeIndUlMsg, ID 0x%.2x, ", VOLUME_IND_UL_MSG);
+    pBuffer[numBytesEncoded] = VOLUME_IND_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), pMsg->volumeLitres);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeRssiIndUlMsg (char * pBuffer,
+                                             RssiIndUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding RssiIndUlMsg, ID 0x%.2x, ", RSSI_IND_UL_MSG);
+    pBuffer[numBytesEncoded] = RSSI_IND_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), pMsg->rssi);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeSerialNumberGetReqDlMsg (char * pBuffer)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding SerialNumberGetReqDlMsg, ID 0x%.2x, ", SERIAL_NUMBER_GET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = SERIAL_NUMBER_GET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), 0); // Encode packing as all
+                                                                    // messages must have a body
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeSerialNumberGetCnfUlMsg (char * pBuffer,
+                                                     SerialNumberGetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding SerialNumberGetCnfUlMsg, ID 0x%.2x, ", SERIAL_NUMBER_GET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = SERIAL_NUMBER_GET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), pMsg->serialNumber);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeReadingIntervalSetReqDlMsg (char * pBuffer,
+                                                           ReadingIntervalSetReqDlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding ReadingIntervalSetReqDlMsg, ID 0x%.2x, ", READING_INTERVAL_SET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = READING_INTERVAL_SET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), pMsg->readingIntervalSeconds);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeReadingIntervalSetCnfUlMsg (char * pBuffer,
+                                                           ReadingIntervalSetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding ReadingIntervalSetCnfUlMsg, ID 0x%.2x, ", READING_INTERVAL_SET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = READING_INTERVAL_SET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), pMsg->readingIntervalSeconds);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeReadingIntervalGetReqDlMsg (char * pBuffer)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding ReadingIntervalGetReqDlMsg, ID 0x%.2x, ", READING_INTERVAL_GET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = READING_INTERVAL_GET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), 0); // Encode packing as all
+                                                                    // messages must have a body
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeReadingIntervalGetCnfUlMsg (char * pBuffer,
+                                                           ReadingIntervalGetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding ReadingIntervalGetCnfUlMsg, ID 0x%.2x, ", READING_INTERVAL_GET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = READING_INTERVAL_GET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), pMsg->readingIntervalSeconds);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeGpioSetReqDlMsg (char * pBuffer,
+                                                GpioSetReqDlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding GpioSetReqDlMsg, ID 0x%.2x, ", GPIO_SET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = GPIO_SET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeGpioState (&(pBuffer[numBytesEncoded]), &(pMsg->gpioState));
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeGpioSetCnfUlMsg (char * pBuffer,
+                                                GpioSetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding GpioSetCnfUlMsg, ID 0x%.2x, ", GPIO_SET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = GPIO_SET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeGpioState (&(pBuffer[numBytesEncoded]), &(pMsg->gpioState));
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeGpioGetReqDlMsg (char * pBuffer,
+                                                GpioGetReqDlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding GpioGetReqDlMsg, ID 0x%.2x, ", LED_GET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = GPIO_GET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeUint32 (&(pBuffer[numBytesEncoded]), (uint32_t) pMsg->gpio);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeGpioGetCnfUlMsg (char * pBuffer,
+                                               GpioGetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding GpioGetCnfUlMsg, ID 0x%.2x, ", LED_GET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = GPIO_GET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeGpioState (&(pBuffer[numBytesEncoded]), &(pMsg->gpioState));
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeLedSetReqDlMsg (char * pBuffer,
+                                               LedSetReqDlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding LedSetReqDlMsg, ID 0x%.2x, ", LED_SET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = LED_SET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), pMsg->onNotOff);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeLedSetCnfUlMsg (char * pBuffer,
+                                               LedSetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding LedSetCnfUlMsg, ID 0x%.2x, ", LED_SET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = LED_SET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), pMsg->onNotOff);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeLedGetReqDlMsg (char * pBuffer)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding LedGetReqDlMsg, ID 0x%.2x, ", LED_GET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = LED_GET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), 0); // Encode packing as all
+                                                                    // messages must have a body
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeLedGetCnfUlMsg (char * pBuffer,
+                                               LedGetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding LedGetCnfUlMsg, ID 0x%.2x, ", LED_GET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = LED_GET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), pMsg->onNotOff);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeFlashSetReqDlMsg (char * pBuffer,
+                                                 FlashSetReqDlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding FlashSetReqDlMsg, ID 0x%.2x, ", FLASH_SET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = FLASH_SET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), pMsg->onNotOff);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeFlashSetCnfUlMsg (char * pBuffer,
+                                                 FlashSetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding FlashSetCnfUlMs, ID 0x%.2x, ", FLASH_SET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = FLASH_SET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), pMsg->onNotOff);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeFlashGetReqDlMsg (char * pBuffer)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding FlashGetReqDlMsg, ID 0x%.2x, ", FLASH_GET_REQ_DL_MSG);
+    pBuffer[numBytesEncoded] = FLASH_GET_REQ_DL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), 0); // Encode packing as all
+                                                                    // messages must have a body
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+uint32_t MessageCodec::encodeFlashGetCnfUlMsg (char * pBuffer,
+                                                 FlashGetCnfUlMsg_t * pMsg)
+{
+    uint32_t numBytesEncoded = 0;
+
+    MESSAGE_CODEC_LOGMSG ("Encoding FlashGetCnfUlMs, ID 0x%.2x, ", FLASH_GET_CNF_UL_MSG);
+    pBuffer[numBytesEncoded] = FLASH_GET_CNF_UL_MSG;
+    numBytesEncoded++;
+    numBytesEncoded += encodeBool (&(pBuffer[numBytesEncoded]), pMsg->onNotOff);
+    MESSAGE_CODEC_LOGMSG ("%d bytes encoded.\n", numBytesEncoded);
+
+    return numBytesEncoded;
+}
+
+// ----------------------------------------------------------------
+// MESSAGE DECODING FUNCTIONS
+// ----------------------------------------------------------------
+MessageCodec::DecodeResult_t MessageCodec::decodeDlMsg (const char ** ppInBuffer,
+                                                            uint32_t sizeInBuffer,
+                                                            DlMsgUnion_t * pOutBuffer)
+{
+    MsgIdDl_t msgId;
+    DecodeResult_t decodeResult = DECODE_RESULT_FAILURE;
+
+    if (sizeInBuffer < MAX_MESSAGE_SIZE)
+    {
+        decodeResult = DECODE_RESULT_INPUT_TOO_SHORT;
+    }
+    else
+    {
+        decodeResult = DECODE_RESULT_UNKNOWN_MSG_ID;
+        // First byte should be a valid DL message ID
+        msgId = (MsgIdDl_t) **ppInBuffer;
+        (*ppInBuffer)++;
+        if (msgId < MAX_NUM_DL_MSGS)
+        {
+            switch (msgId)
+            {
+                case REBOOT_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_REBOOT_REQ_DL_MSG;
+                    pOutBuffer->rebootReqDlMsg.devModeOnNotOff = decodeBool (ppInBuffer);
+                break;
+                case SERIAL_NUMBER_GET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_SERIAL_NUMBER_GET_REQ_DL_MSG;
+                    decodeUint32 (ppInBuffer);  // Must decode the rest of the dummy message;
+                break;
+                case READING_INTERVAL_SET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_READING_INTERVAL_SET_REQ_DL_MSG;
+                    pOutBuffer->readingIntervalSetReqDlMsg.readingIntervalSeconds = decodeUint32 (ppInBuffer);
+                break;
+                case READING_INTERVAL_GET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_READING_INTERVAL_GET_REQ_DL_MSG;
+                    decodeUint32 (ppInBuffer);  // Must decode the rest of the dummy message;
+                break;
+                case GPIO_SET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_GPIO_SET_REQ_DL_MSG;
+                    decodeGpioState (&(pOutBuffer->gpioSetReqDlMsg.gpioState), ppInBuffer);
+                break;
+                case GPIO_GET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_GPIO_GET_REQ_DL_MSG;
+                    pOutBuffer->gpioGetReqDlMsg.gpio = (uint8_t) decodeUint32 (ppInBuffer);
+                break;
+                case LED_SET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_LED_SET_REQ_DL_MSG;
+                    pOutBuffer->ledSetReqDlMsg.onNotOff = decodeBool (ppInBuffer);
+                break;
+                case LED_GET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_LED_GET_REQ_DL_MSG;
+                    decodeUint32 (ppInBuffer);  // Must decode the rest of the dummy message;
+                break;
+                case FLASH_SET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_FLASH_SET_REQ_DL_MSG;
+                    pOutBuffer->flashSetReqDlMsg.onNotOff = decodeBool (ppInBuffer);
+                break;
+                case FLASH_GET_REQ_DL_MSG:
+                    decodeResult = DECODE_RESULT_FLASH_GET_REQ_DL_MSG;
+                    decodeUint32 (ppInBuffer);  // Must decode the rest of the dummy message;
+                break;
+                default:
+                // The decodeResult will be left as Unknown message
+                break;
+            }
+        }
+    }
+
+    return decodeResult;
+}
+
+MessageCodec::DecodeResult_t MessageCodec::decodeUlMsg (const char ** ppInBuffer,
+                                                            uint32_t sizeInBuffer,
+                                                            UlMsgUnion_t * pOutBuffer)
+{
+    MsgIdUl_t msgId;
+    DecodeResult_t decodeResult = DECODE_RESULT_FAILURE;
+
+    if (sizeInBuffer < MAX_MESSAGE_SIZE)
+    {
+        decodeResult = DECODE_RESULT_INPUT_TOO_SHORT;
+    }
+    else
+    {
+        decodeResult = DECODE_RESULT_UNKNOWN_MSG_ID;
+        // First byte should be a valid UL message ID
+        msgId = (MsgIdUl_t) **ppInBuffer;
+        (*ppInBuffer)++;
+        if (msgId < MAX_NUM_UL_MSGS)
+        {
+            switch (msgId)
+            {
+                case INIT_IND_UL_MSG:
+                    decodeResult = DECODE_RESULT_INIT_IND_UL_MSG;
+                    pOutBuffer->initIndUlMsg.wakeUpCode = (WakeUpCode_t) decodeUint32 (ppInBuffer);
+                break;
+                case SERIAL_NUMBER_IND_UL_MSG:
+                    decodeResult = DECODE_RESULT_SERIAL_NUMBER_IND_UL_MSG;
+                    pOutBuffer->serialNumberIndUlMsg.serialNumber = decodeUint32 (ppInBuffer);
+                break;
+                case VOLUME_IND_UL_MSG:
+                    decodeResult = DECODE_RESULT_VOLUME_IND_UL_MSG;
+                    pOutBuffer->volumeIndUlMsg.volumeLitres = decodeUint32 (ppInBuffer);
+                break;
+                case RSSI_IND_UL_MSG:
+                    decodeResult = DECODE_RESULT_RSSI_IND_UL_MSG;
+                    pOutBuffer->rssiIndUlMsg.rssi = decodeUint32 (ppInBuffer);
+                break;
+                case SERIAL_NUMBER_GET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_SERIAL_NUMBER_GET_CNF_UL_MSG;
+                    pOutBuffer->serialNumberCnfUlMsg.serialNumber = decodeUint32 (ppInBuffer);
+                break;
+                case READING_INTERVAL_SET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_READING_INTERVAL_SET_CNF_UL_MSG;
+                    pOutBuffer->readingIntervalSetCnfUlMsg.readingIntervalSeconds = decodeUint32 (ppInBuffer);
+                break;
+                case READING_INTERVAL_GET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_READING_INTERVAL_GET_CNF_UL_MSG;
+                    pOutBuffer->readingIntervalGetCnfUlMsg.readingIntervalSeconds = decodeUint32 (ppInBuffer);
+                break;
+                case GPIO_SET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_GPIO_SET_CNF_UL_MSG;
+                    pOutBuffer->gpioSetCnfUlMsg = decodeUint32 (ppInBuffer); // Decode as an int32 for later unpacking
+                break;
+                case GPIO_GET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_GPIO_GET_CNF_UL_MSG;
+                    pOutBuffer->gpioGetCnfUlMsg = decodeUint32 (ppInBuffer); // Decode as an int32 for later unpacking
+                break;
+                case LED_SET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_LED_SET_CNF_UL_MSG;
+                    pOutBuffer->ledSetCnfUlMsg.onNotOff = decodeBool (ppInBuffer);
+                break;
+                case LED_GET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_LED_GET_CNF_UL_MSG;
+                    pOutBuffer->ledGetCnfUlMsg.onNotOff = decodeBool (ppInBuffer);
+                break;
+                case FLASH_SET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_FLASH_SET_CNF_UL_MSG;
+                    pOutBuffer->flashSetCnfUlMsg.onNotOff = decodeBool (ppInBuffer);
+                break;
+                case FLASH_GET_CNF_UL_MSG:
+                    decodeResult = DECODE_RESULT_FLASH_GET_CNF_UL_MSG;
+                    pOutBuffer->flashGetCnfUlMsg.onNotOff = decodeBool (ppInBuffer);
+                break;
+                default:
+                // The decodeResult will be left as Unknown message
+                break;
+            }
+        }
+    }
+
+    return decodeResult;
+}
+
+// ----------------------------------------------------------------
+// MISC FUNCTIONS
+// ----------------------------------------------------------------
+
+// Log debug messages
+void MessageCodec::logMsg (const char * pFormat, ...)
+{
+    char buffer[MAX_DEBUG_MESSAGE_LEN];
+
+    va_list args;
+    va_start (args, pFormat);
+    vsnprintf (buffer, sizeof (buffer), pFormat, args);
+    va_end (args);
+#ifdef WIN32
+    if (MessageCodec::mp_guiPrintToConsole)
+    {
+        (*MessageCodec::mp_guiPrintToConsole) (buffer);
+    }
+#else
+    // Must be on ARM
+    printf (buffer);
+#endif
+}
+
+void  MessageCodec::initDll (void (*guiPrintToConsole) (const char *))
+{
+#ifdef WIN32
+    mp_guiPrintToConsole = guiPrintToConsole; 
+    // This is the signal to the GUI that we're done with initialisation
+    logMsg ("MessageCodec::ready.\n");
+#endif
+}
+
+// End Of File
diff -r 000000000000 -r 5c46cb3be899 IotMeterMsgs.hpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IotMeterMsgs.hpp	Fri May 22 11:41:38 2015 +0000
@@ -0,0 +1,281 @@
+/* C027N/water-meter message definitions for Water Meter Demo
+ * 
+ * Copyright (C) u-blox Melbourn Ltd
+ * u-blox Melbourn Ltd, Melbourn, UK
+ * 
+ * All rights reserved.
+ *
+ * This source file is the sole property of u-blox Melbourn Ltd.
+ * Reproduction or utilization of this source in whole or part is
+ * forbidden without the written consent of u-blox Melbourn Ltd.
+ */
+
+#ifndef IOT_METER_MSGS_HPP
+#define IOT_METER_MSGS_HPP
+
+/**
+ * @file iot_meter_msgs.h
+ * This file defines the messages sent between the
+ * C027N/water-meter device and a PC app for the MWC demo
+ * 2015.
+ *
+ * The message format is as follows:
+ *
+ * uint8_t   id
+ * uint32_t  value
+ *
+ * The IDs are defined in an enumerated type and there are
+ * separately defined ID sets for the downlink and uplink
+ * directions (i.e. they may overlap).  When transmitted the
+ * messages are packed such that the 32-bit content byte
+ * immediately follows the 8-bit ID with no packing, resulting
+ * in a fixed message length of 5 bytes.  Multiple fixed
+ * length messages may be packed into a datagram, hence it can
+ * be assumed that a datagram of length < 10 bytes contains
+ * one message, a datagram of length < 15 bytes contains two
+ * messages, etc.
+ *
+ * Boolean values are expressed as uint32_t zero (false) or
+ * uint32_t one (true).  For multibyte values the highest
+ * value bytes is stored first, so a message with ID 0xa5 and
+ * value 0x12345678 would be stored in the datagram as:
+ *
+ * Location: 0 1 2 3 4
+ * Contents: a512345678
+ *
+ * There are some messages (Gpio setters for instance) which
+ * encode more than a single value into the value field.  See
+ * the individual encode/decode functions for how these values
+ * are packed.
+ */
+ 
+// ----------------------------------------------------------------
+// GENERAL COMPILE-TIME CONSTANTS
+// ----------------------------------------------------------------
+
+/// The maximum length of a single messages in bytes
+#define MAX_MESSAGE_SIZE 5
+ 
+/// The default meter reading interval.
+#define DEFAULT_READING_INTERVAL_SECONDS 10
+
+
+// ----------------------------------------------------------------
+// TYPES
+// ----------------------------------------------------------------
+
+/// The wake up code sent from the device
+typedef enum WakeUpCodeTag_t
+{
+    WAKE_UP_CODE_OK,                   //!< A good wake-up, no problems.
+    WAKE_UP_CODE_WATER_METER_PROBLEM,  //!< Wake-up after assert due to
+                                       //! problems reading the water meter.
+    WAKE_UP_CODE_AT_COMMAND_PROBLEM,   //!< Wake-up after assert due to
+                                       //! problems with AT commands.
+    WAKE_UP_CODE_NETWORK_SEND_PROBLEM, //!< Wake-up after assert due to
+                                       //! problems sending to the network.
+    WAKE_UP_CODE_MEMORY_ALLOC_PROBLEM, //!< Wake-up after assert due to
+                                       //! memory allocation issues.
+    WAKE_UP_CODE_PROTOCOL_PROBLEM,     //!< Wake-up after assert due to
+                                       //! a protocol problem.
+    WAKE_UP_CODE_GENERIC_FAILURE,      //!< Wake-up after a generic failure.
+    WAKE_UP_CODE_REBOOT,               //!< Waking up after a commanded reboot.
+    MAX_NUM_WAKE_UP_CODES              //!< The maximum number of
+                                       //! decode results.
+} WakeUpCode_t;
+
+// ----------------------------------------------------------------
+// MESSAGE BODIES
+// ----------------------------------------------------------------
+ 
+/// InitIndUlMsg_t. Sent at power-on of the device. Indicates that the
+// device has been initialised.
+// After transmission of this message meter readings will be returned
+// at the requested rate (or DEFAULT_READING_INTERVAL_SECONDS if no
+// InitReqDlMsg_t has been received by the device).
+typedef struct InitIndUlMsgTag_t
+{
+    WakeUpCode_t wakeUpCode; //!< A wake-up code from the device.
+} InitIndUlMsg_t;
+
+/// RebootReqDlMsg_t. Sent to reboot the device and set the development
+// mode on or off.  By default development mode is OFF.
+typedef struct RebootReqDlMsgTag_t
+{
+    bool devModeOnNotOff; //!< If true development mode is on, else it is off.
+} RebootReqDlMsg_t;
+
+/// SerialNumberGetCnfUlMsg_t. Sent in response to a SerialNumberGetReqDlMsg_t (which
+// is empty and so not represented here).
+typedef struct SerialNumberGetCnfUlMsgTag_t
+{
+  uint32_t serialNumber; //!< The serial number of the meter.
+} SerialNumberGetCnfUlMsg_t;
+
+/// SerialNumberIndUlMsg_t. Sent at power-on of the device and in response to
+// an InitReqDlMsg_t. Indicates the serial number of the device.
+typedef struct SerialNumberIndUlMsgTag_t
+{
+  uint32_t serialNumber; //!< The serial number of the meter.
+} SerialNumberIndUlMsg_t;
+
+/// VolumeIndUlMsg_t. The current meter reading.
+typedef struct VolumeIndUlMsgTag_t
+{
+  uint32_t volumeLitres;
+} VolumeIndUlMsg_t;
+
+/// RssiIndUlMsg_t. The current RSSI reading.
+typedef struct RssiIndUlMsgTag_t
+{
+  uint32_t rssi; //!< The RSSI reading in arbitrary units, range 0 to 255.
+} RssiIndUlMsg_t;
+
+/// ReadingIntervalSetReqDlMsg_t. Set the meter reading interval.
+typedef struct ReadingIntervalSetReqDlMsgTag_t
+{
+  uint32_t readingIntervalSeconds; //!< The interval at which the device
+                                   //! should send readings.
+} ReadingIntervalSetReqDlMsg_t;
+
+/// ReadingIntervalSetCnfUlMsg_t. Sent in response to a
+// ReadingIntervalSetReqDlMsg_t.
+// After transmission of this message meter readings will be returned
+// at the requested rate (or DEFAULT_READING_INTERVAL_SECONDS if no
+// command setting it otherwise has been received by the device).
+typedef struct ReadingIntervalSetCnfUlMsgTag_t
+{
+  uint32_t readingIntervalSeconds; //!< The interval at which readings are sent.
+} ReadingIntervalSetCnfUlMsg_t;
+
+/// ReadingIntervalGetCnfUlMsg_t. Sent in response to a
+// ReadingIntervalGetReqDlMsg_t (which is empty and so not represented
+// here).
+// After transmission of this message meter readings will be returned
+// at the requested rate (or DEFAULT_READING_INTERVAL_SECONDS if no
+// command setting it otherwise has been received by the device).
+typedef struct ReadingIntervalGetCnfUlMsgTag_t
+{
+    uint32_t readingIntervalSeconds; //!< The interval at which readings are sent.
+} ReadingIntervalGetCnfUlMsg_t;
+
+/// GpioState_t.  Data concerning how a GPIO is set up.
+typedef struct GpioStateTag_t
+{
+    uint8_t gpio;        //!< The GPIO in question: 0 for D0, 1 for D1, etc.
+    bool inputNotOutput; //!< true if this is an input, else it is an output.
+    bool onNotOff;       //!< If the GPIO is an output then this gives its state.
+                         //! If the GPIO is an input this is not set.
+} GpioState_t;
+
+/// GpioSetReqDlMsg_t. Set the state of a GPIO on the C027N board.
+typedef struct GpioSetReqDlMsgTag_t
+{
+    GpioState_t gpioState; //!< The state of the GPIO.
+} GpioSetReqDlMsg_t;
+
+/// GpioSetCnfUlMsg_t.  Response to GpioSetReqDlMsg_t.
+typedef struct GpioSetCnfUlMsgTag_t
+{
+    GpioState_t gpioState; //!< The state of the GPIO.
+} GpioSetCnfUlMsg_t;
+
+/// GpioGetReqDlMsg_t. Gets the state of a GPIO on the C027N board.
+typedef struct GpioGetReqDlMsgTag_t
+{
+    uint8_t gpio;        //!< The GPIO to get.
+} GpioGetReqDlMsg_t;
+
+/// GpioGetCnfUlMsg_t. Sent in response to a GpioGetReqDlMsg.
+typedef struct GpioGetCnfUlMsgTag_t
+{
+    GpioState_t gpioState; //!< The state of the GPIO.
+} GpioGetCnfUlMsg_t;
+
+/// LedSetReqDlMsg_t. Set the steady state of the red LED on
+// the C027N board.
+typedef struct LedSetReqDlMsgTag_t
+{
+    bool onNotOff; //!< Make the steady state ON if true, otherwise OFF.
+                   //! OFF is the default state.
+} LedSetReqDlMsg_t;
+
+/// LedSetCnfUlMsg_t.  Response to LedSetReqDlMsg_t.
+typedef struct LedSetCnfUlMsgTag_t
+{
+    bool onNotOff; //!< The LED is steady-state ON if true, otherwise
+                   //! OFF.
+} LedSetCnfUlMsg_t;
+
+/// LedGetCnfUlMsg_t. Sent in response to an LedGetReqDlMsg
+// (which is an empty ID and so not represented here).
+typedef struct LedGetCnfUlMsgTag_t
+{
+    bool onNotOff; //!< The steady state is ON if true, otherwise OFF.
+} LedGetCnfUlMsg_t;
+
+/// FlashSetReqDlMsg_t. Set the red LED to flash when a reading is
+// being sent (or not).  If LedSetReqDlMsg_t has been set to ON the
+// flash will be 'inverted', i.e. the red LED will blink off when a
+// reading is being transmitted instead.
+typedef struct FlashSetReqDlMsgTag_t
+{
+    bool onNotOff; //!< If true then the red LED will flash when a
+                   //! reading is being sent, else it will remain in
+                   //! steady state. The default is to flash when a
+                   //! reading is being sent.
+} FlashSetReqDlMsg_t;
+
+/**
+ * FlashSetCnfUlMsg_t. Response to FlashSetReqDlMsg_t.
+ */
+typedef struct FlashSetCnfUlMsgTag_t
+{
+    bool onNotOff; //!< If true then the red LED flashes when a
+                   //! reading is being sent, else it will remain in
+                   //! steady state.
+} FlashSetCnfUlMsg_t;
+
+/// FlashGetCnfUlMsg_t. Sent in response to a FlashGetReqDlMsg
+// (which is an empty ID and so not represented here).
+typedef struct FlashGetCnfUlMsgTag_t
+{
+    bool onNotOff; //!< The steady state is ON if true, otherwise OFF.
+} FlashGetCnfUlMsg_t;
+
+// ----------------------------------------------------------------
+// MESSAGE UNIONS
+// ----------------------------------------------------------------
+
+/// Union of all downlink messages.
+typedef union DlMsgUnionTag_t
+{
+    RebootReqDlMsg_t rebootReqDlMsg;
+    ReadingIntervalSetReqDlMsg_t readingIntervalSetReqDlMsg;
+    GpioSetReqDlMsg_t gpioSetReqDlMsg;
+    GpioGetReqDlMsg_t gpioGetReqDlMsg;
+    LedSetReqDlMsg_t ledSetReqDlMsg;
+    FlashSetReqDlMsg_t flashSetReqDlMsg;
+} DlMsgUnion_t;
+
+/// Union of all uplink messages.
+typedef union UlMsgUnionTag_t
+{
+    InitIndUlMsg_t initIndUlMsg;
+    VolumeIndUlMsg_t volumeIndUlMsg;
+    RssiIndUlMsg_t rssiIndUlMsg;
+    SerialNumberIndUlMsg_t serialNumberIndUlMsg;
+    SerialNumberGetCnfUlMsg_t serialNumberCnfUlMsg;
+    ReadingIntervalSetCnfUlMsg_t readingIntervalSetCnfUlMsg;
+    ReadingIntervalGetCnfUlMsg_t readingIntervalGetCnfUlMsg;
+    uint32_t gpioSetCnfUlMsg; // These are left packed inside a uint32_t so that
+    uint32_t gpioGetCnfUlMsg; // they can be passed to C# without a struct.
+    LedSetCnfUlMsg_t ledSetCnfUlMsg;
+    LedGetCnfUlMsg_t ledGetCnfUlMsg;
+    FlashSetCnfUlMsg_t flashSetCnfUlMsg;
+    FlashGetCnfUlMsg_t flashGetCnfUlMsg;
+} UlMsgUnion_t;
+
+#endif
+
+// End Of File