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.

IotMeterMsgCodec.cpp

Committer:
RobMeades
Date:
2015-05-22
Revision:
0:5c46cb3be899

File content as of revision 0:5c46cb3be899:

/* 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