BERG Cloud / BERGCloud

Dependents:   LittleCounter-Example

Revision:
0:b4ccb530b9eb
Child:
5:2e04a8b3fc25
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudMessageBase.cpp	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,1361 @@
+/*
+
+BERGCloud message pack/unpack
+
+Based on MessagePack http://msgpack.org/
+
+Copyright (c) 2013 BERG Cloud Ltd. http://bergcloud.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+#include <stddef.h> /* For NULL */
+#include <string.h> /* For memcpy() */
+#include "BERGCloudMessageBase.h"
+
+BERGCloudMessageBase::BERGCloudMessageBase(void)
+{
+}
+
+BERGCloudMessageBase::~BERGCloudMessageBase(void)
+{
+}
+
+#define _MP_FIXNUM_POS_MIN  0x00
+#define _MP_FIXNUM_POS_MAX  0x7f
+#define _MP_FIXMAP_MIN      0x80
+#define _MP_FIXMAP_MAX      0x8f
+#define _MP_FIXARRAY_MIN    0x90
+#define _MP_FIXARRAY_MAX    0x9f
+#define _MP_FIXRAW_MIN      0xa0
+#define _MP_FIXRAW_MAX      0xbf
+#define _MP_NIL             0xc0
+#define _MP_BOOL_FALSE      0xc2
+#define _MP_BOOL_TRUE       0xc3
+#define _MP_FLOAT           0xca
+#define _MP_DOUBLE          0xcb
+#define _MP_UINT8           0xcc
+#define _MP_UINT16          0xcd
+#define _MP_UINT32          0xce
+#define _MP_UINT64          0xcf
+#define _MP_INT8            0xd0
+#define _MP_INT16           0xd1
+#define _MP_INT32           0xd2
+#define _MP_INT64           0xd3
+#define _MP_RAW16           0xda
+#define _MP_RAW32           0xdb
+#define _MP_ARRAY16         0xdc
+#define _MP_ARRAY32         0xdd
+#define _MP_MAP16           0xde
+#define _MP_MAP32           0xdf
+#define _MP_FIXNUM_NEG_MIN  0xe0
+#define _MP_FIXNUM_NEG_MAX  0xff
+
+#define _MAX_FIXRAW         (_MP_FIXRAW_MAX - _MP_FIXRAW_MIN)
+#define _MAX_FIXARRAY       (_MP_FIXARRAY_MAX - _MP_FIXARRAY_MIN)
+#define _MAX_FIXMAP         (_MP_FIXMAP_MAX - _MP_FIXMAP_MIN)
+
+uint16_t BERGCloudMessageBase::strlen(const char *string)
+{
+  uint16_t strLen = 0;
+
+  /* Find string length */
+  if (string != NULL)
+  {
+    while ((strLen < UINT16_MAX) && (*string != '\0'))
+    {
+       string++;
+       strLen++;
+    }
+  }
+
+  return strLen;
+}
+
+bool BERGCloudMessageBase::strcompare(const char *s1, const char *s2)
+{
+  uint16_t count = 0;
+
+  if ((s1 == NULL) || (s2==NULL))
+  {
+    return false;
+  }
+
+  while ((*s1 != '\0') && (*s2 != '\0'))
+  {
+    if (*s1++ != *s2++)
+    {
+        return false;
+    }
+
+    if (count++ == UINT16_MAX)
+    {
+        return false;
+    }
+  }
+
+  /* identical */
+  return true;
+}
+
+
+/*
+    Pack methods
+*/
+
+bool BERGCloudMessageBase::pack(uint8_t n)
+{
+  if (!available(sizeof(n) + 1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  add(_MP_UINT8);
+  add(n);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack(uint16_t n)
+{
+  if (!available(sizeof(n) + 1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  add(_MP_UINT16);
+  add((uint8_t)(n >> 8));
+  add((uint8_t)n);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack(uint32_t n)
+{
+  if (!available(sizeof(n) + 1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  add(_MP_UINT32);
+  add((uint8_t)(n >> 24));
+  add((uint8_t)(n >> 16));
+  add((uint8_t)(n >> 8));
+  add((uint8_t)n);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack(int8_t n)
+{
+  if (!available(sizeof(n) + 1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  add(_MP_INT8);
+  add((uint8_t)n);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack(int16_t n)
+{
+  if (!available(sizeof(n) + 1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  add(_MP_INT16);
+  add((uint8_t)(n >> 8));
+  add((uint8_t)n);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack(int32_t n)
+{
+  if (!available(sizeof(n) + 1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  add(_MP_INT32);
+  add((uint8_t)(n >> 24));
+  add((uint8_t)(n >> 16));
+  add((uint8_t)(n >> 8));
+  add((uint8_t)n);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack(float n)
+{
+  uint32_t data;
+
+  if (!available(sizeof(n) + 1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  /* Convert to data */
+  memcpy(&data, &n, sizeof(float));
+
+  add(_MP_FLOAT);
+  add((uint8_t)(data >> 24));
+  add((uint8_t)(data >> 16));
+  add((uint8_t)(data >> 8));
+  add((uint8_t)data);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack(bool n)
+{
+  /*
+      Note that Arduino redefines 'true' and 'false' in Arduino.h.
+      You can undefine them in your code to make them type 'bool' again:
+      #undef true
+      #undef false
+  */
+
+  if (!available(1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  add(n ? _MP_BOOL_TRUE : _MP_BOOL_FALSE);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack_nil(void)
+{
+  if (!available(1))
+  {
+    _LOG_PACK_ERROR_NO_SPACE;
+    return false;
+  }
+
+  add(_MP_NIL);
+  return true;
+}
+
+bool BERGCloudMessageBase::pack_array(uint16_t items)
+{
+  if (items <= _MAX_FIXARRAY)
+  {
+    /* Use fix array */
+    if (!available(items + 1))
+    {
+      _LOG_PACK_ERROR_NO_SPACE;
+      return false;
+    }
+
+    add(_MP_FIXARRAY_MIN + items);
+  }
+  else
+  {
+    /* Use array 16 */
+    if (!available(items + 1 + sizeof(uint16_t)))
+    {
+      _LOG_PACK_ERROR_NO_SPACE;
+      return false;
+    }
+
+    add(_MP_ARRAY16);
+    add((uint8_t)(items >> 8));
+    add((uint8_t)items);
+  }
+
+  return true;
+}
+
+bool BERGCloudMessageBase::pack_map(uint16_t items)
+{
+  if (items <= _MAX_FIXMAP)
+  {
+    /* Use fix map */
+    if (!available(items + 1))
+    {
+      _LOG_PACK_ERROR_NO_SPACE;
+      return false;
+    }
+
+    add(_MP_FIXMAP_MIN + items);
+  }
+  else
+  {
+    /* Use map 16 */
+    if (!available(items + 1 + sizeof(uint16_t)))
+    {
+      _LOG_PACK_ERROR_NO_SPACE;
+      return false;
+    }
+
+    add(_MP_MAP16);
+    add((uint8_t)(items >> 8));
+    add((uint8_t)items);
+  }
+
+  return true;
+}
+
+bool BERGCloudMessageBase::pack(uint8_t *data, uint16_t sizeInBytes)
+{
+  /* Pack data */
+  if (!pack_raw_header(sizeInBytes))
+  {
+    return false;
+  }
+
+  return pack_raw_data(data, sizeInBytes);
+}
+
+bool BERGCloudMessageBase::pack(const char *string)
+{
+  /* Pack a null-terminated C string */
+  uint16_t strLen;
+
+  strLen = BERGCloudMessageBase::strlen(string);
+
+  return pack((uint8_t *)string, strLen);
+}
+
+/* Separate header and data methods are provided for raw data*/
+/* so that Arduino strings may be packed without having to create */
+/* a temporary buffer first. */
+
+bool BERGCloudMessageBase::pack_raw_header(uint16_t sizeInBytes)
+{
+  if (sizeInBytes <= _MAX_FIXRAW)
+  {
+    /* Use fix raw */
+    if (!available(sizeInBytes + 1))
+    {
+      _LOG_PACK_ERROR_NO_SPACE;
+      return false;
+    }
+
+    add(_MP_FIXRAW_MIN + sizeInBytes);
+  }
+  else
+  {
+    /* Use raw 16 */
+    if (!available(sizeInBytes + 1 + sizeof(uint16_t)))
+    {
+      _LOG_PACK_ERROR_NO_SPACE;
+      return false;
+    }
+
+    add(_MP_RAW16);
+    add((uint8_t)(sizeInBytes >> 8));
+    add((uint8_t)sizeInBytes);
+  }
+
+  return true;
+}
+
+bool BERGCloudMessageBase::pack_raw_data(uint8_t *data, uint16_t sizeInBytes)
+{
+  /* Add data */
+  while (sizeInBytes-- > 0)
+  {
+    add(*data++);
+  }
+
+  return true;
+}
+
+/*
+    Unpack methods
+*/
+
+bool BERGCloudMessageBase::unpack_peek(uint8_t& messagePackType)
+{
+  return peek(&messagePackType);
+}
+
+#ifdef BERGCLOUD_LOG
+bool BERGCloudMessageBase::unpack_peek(void)
+{
+  uint8_t type;
+
+  if (!peek(&type))
+  {
+    return false;
+  }
+
+  if (type <= _MP_FIXNUM_POS_MAX)
+  {
+    _LOG("Positive integer\r\n");
+    return true;
+  }
+
+  if (IN_RANGE(type, _MP_FIXNUM_NEG_MIN, _MP_FIXNUM_NEG_MAX))
+  {
+    _LOG("Negative integer\r\n");
+    return true;
+  }
+
+  if ((type == _MP_MAP16) || (type == _MP_MAP32) || IN_RANGE(type, _MP_FIXMAP_MIN, _MP_FIXMAP_MAX))
+  {
+    _LOG("Map\r\n");
+    return true;
+  }
+
+  if ((type == _MP_ARRAY16) || (type == _MP_ARRAY32) || IN_RANGE(type, _MP_FIXARRAY_MIN, _MP_FIXARRAY_MAX))
+  {
+    _LOG("Array\r\n");
+    return true;
+  }
+
+  if ((type == _MP_RAW16) || (type == _MP_RAW32) || IN_RANGE(type, _MP_FIXRAW_MIN, _MP_FIXRAW_MAX))
+  {
+    _LOG("Raw\r\n");
+    return true;
+  }
+
+  if ((type ==_MP_UINT8) || (type == _MP_UINT16) || (type == _MP_UINT32) || (type == _MP_UINT64))
+  {
+    _LOG("Unsigned integer\r\n");
+    return true;
+  }
+
+  if ((type ==_MP_INT8) || (type == _MP_INT16) || (type == _MP_INT32) || (type == _MP_INT64))
+  {
+    _LOG("Signed integer\r\n");
+    return true;
+  }
+
+  if (type == _MP_NIL)
+  {
+    _LOG("Nil\r\n");
+    return true;
+  }
+
+  if (type == _MP_BOOL_FALSE)
+  {
+    _LOG("Boolean false\r\n");
+    return true;
+  }
+
+  if (type == _MP_BOOL_TRUE)
+  {
+    _LOG("Boolean true\r\n");
+    return true;
+  }
+
+  if (type == _MP_FLOAT)
+  {
+    _LOG("Float\r\n");
+    return true;
+  }
+
+  if (type == _MP_DOUBLE)
+  {
+    _LOG("Double\r\n");
+    return true;
+  }
+
+  _LOG("Unknown\r\n");
+  return false;
+}
+
+void BERGCloudMessageBase::print(void)
+{
+  uint16_t last_read;
+
+  /* Remember the current read position in the raw data */
+  last_read = bytesRead;
+
+  /* Start reading from the beginning of the data */
+  restart();
+
+  /* Print all items */
+  while(unpack_peek())
+  {
+    unpack_skip();
+  }
+
+  /* Return to the last position */
+  bytesRead = last_read;
+}
+
+void BERGCloudMessageBase::print_bytes(void)
+{
+  uint16_t size = used();
+  uint8_t *data = ptr();
+
+  while (size-- > 0)
+  {
+    _LOG_HEX(*data);
+    _LOG(" ");
+    data++;
+  }
+  _LOG("\r\n");
+}
+#endif
+
+bool BERGCloudMessageBase::getUnsignedInteger(uint32_t *value, uint8_t maxBytes)
+{
+  /* Try to decode the next messagePack item as an unsigned integer */
+  /* of less than or equal to 'maxBytes' size in bytes. */
+
+  uint8_t type;
+
+  if (maxBytes == 0)
+  {
+    /* Invalid */
+    return false;
+  }
+
+  /* Look at next type */
+  if (!peek(&type))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (type <= _MP_FIXNUM_POS_MAX)
+  {
+    /* Read fix num value */
+    *value = read();
+
+    /* Success */
+    return true;
+  }
+
+  if (type == _MP_UINT8)
+  {
+    if (!remaining(sizeof(uint8_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 8-bit unsigned integer */
+    *value = read();
+
+    /* Success */
+    return true;
+  }
+
+  if ((type == _MP_UINT16) && (maxBytes >= sizeof(uint16_t)))
+  {
+    if (!remaining(sizeof(uint16_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 16-bit unsigned integer */
+    *value = read();
+    *value = *value << 8;
+    *value |= read();
+
+    /* Success */
+    return true;
+  }
+
+  if ((type == _MP_UINT32) && (maxBytes >= sizeof(uint32_t)))
+  {
+    if (!remaining(sizeof(uint32_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 32-bit unsigned integer */
+    *value = read();
+    *value = *value << 8;
+    *value |= read();
+    *value = *value << 8;
+    *value |= read();
+    *value = *value << 8;
+    *value |= read();
+
+    /* Success */
+    return true;
+  }
+
+  /* Can't convert this type */
+  _LOG_UNPACK_ERROR_TYPE;
+  return false;
+}
+
+bool BERGCloudMessageBase::getSignedInteger(int32_t *value, uint8_t maxBytes)
+{
+  /* Try to decode the next messagePack item as an signed integer */
+  /* of less than or equal to 'maxBytes' size in bytes. */
+
+  uint8_t type;
+
+  if (maxBytes == 0)
+  {
+    /* Invalid */
+    return false;
+  }
+
+  /* Look at next type */
+  if (!peek(&type))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (type <= _MP_FIXNUM_POS_MAX)
+  {
+    /* Read fix num value */
+    *value = (int32_t)read();
+
+    /* Success */
+    return true;
+  }
+
+  if (IN_RANGE(type, _MP_FIXNUM_NEG_MIN, _MP_FIXNUM_NEG_MAX))
+  {
+    /* Read fix num value */
+    *value = (int32_t)read();
+
+    /* Success */
+    return true;
+  }
+
+  if (type == _MP_INT8)
+  {
+    if (!remaining(sizeof(int8_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 8-bit signed integer */
+    *value = (int32_t)read();
+    return true;
+  }
+
+  if ((type == _MP_INT16) && (maxBytes >= sizeof(int16_t)))
+  {
+    if (!remaining(sizeof(int16_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 16-bit signed integer */
+    *value = read();
+    *value = *value << 8;
+    *value |= read();
+
+    /* Success */
+    return true;
+  }
+
+  if ((type == _MP_INT32) && (maxBytes >= sizeof(int32_t)))
+  {
+    if (!remaining(sizeof(int32_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 32-bit signed integer */
+    *value = read();
+    *value = *value << 8;
+    *value |= read();
+    *value = *value << 8;
+    *value |= read();
+    *value = *value << 8;
+    *value |= read();
+
+    /* Success */
+    return true;
+  }
+
+  /* Can't convert this type */
+  _LOG_UNPACK_ERROR_TYPE;
+  return false;
+}
+
+bool BERGCloudMessageBase::unpack_skip(void)
+{
+  /* Skip next item */
+
+  uint8_t type;
+  uint32_t bytesToSkip = 0;
+
+  /* Must be at least one byte of data */
+  if (!remaining(sizeof(uint8_t)))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  /* Read type */
+  type = read();
+
+  if ((type == _MP_UINT8) || (type == _MP_INT8))
+  {
+    bytesToSkip = 1;
+  }
+  else if ((type == _MP_UINT16) || (type == _MP_INT16))
+  {
+    bytesToSkip = 2;
+  }
+  else if ((type == _MP_UINT32) || (type == _MP_INT32) || (type == _MP_FLOAT))
+  {
+    bytesToSkip = 4;
+  }
+  else if ((type == _MP_UINT64) || (type == _MP_INT64) || (type == _MP_DOUBLE))
+  {
+    bytesToSkip = 8;
+  }
+  else if ((type == _MP_ARRAY16) || (type == _MP_MAP16))
+  {
+    /* TODO: This could skip all of the array/map elements too. */
+    bytesToSkip = 2;
+  }
+  else if ((type == _MP_ARRAY32) || (type == _MP_MAP32))
+  {
+    /* TODO: This could skip all of the array/map elements too. */
+    bytesToSkip = 4;
+  }
+  else if (type == _MP_RAW16)
+  {
+    if (!remaining(sizeof(uint16_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read 16-bit unsigned integer, data size */
+    bytesToSkip = read();
+    bytesToSkip = bytesToSkip << 8;
+    bytesToSkip |= read();
+  }
+  else if (type == _MP_RAW32)
+  {
+    if (!remaining(sizeof(uint32_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* read 32-bit unsigned integer, data size */
+    bytesToSkip = read();
+    bytesToSkip = bytesToSkip << 8;
+    bytesToSkip |= read();
+    bytesToSkip = bytesToSkip << 8;
+    bytesToSkip |= read();
+    bytesToSkip = bytesToSkip << 8;
+    bytesToSkip |= read();
+  }
+  else if (IN_RANGE(type, _MP_FIXRAW_MIN, _MP_FIXRAW_MAX))
+  {
+    bytesToSkip = type - _MP_FIXRAW_MIN;
+  }
+
+  if (!remaining(bytesToSkip))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  while (bytesToSkip-- > 0)
+  {
+    /* Discard data */
+    read();
+  }
+
+  /* Success */
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(uint8_t& n)
+{
+  uint32_t temp;
+
+  if (!getUnsignedInteger(&temp, sizeof(uint8_t)))
+  {
+    return false;
+  }
+
+  n = (uint8_t)temp;
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(uint16_t& n)
+{
+  uint32_t temp;
+
+  if (!getUnsignedInteger(&temp, sizeof(uint16_t)))
+  {
+    return false;
+  }
+
+  n = (uint16_t)temp;
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(uint32_t& n)
+{
+  uint32_t temp;
+
+  if (!getUnsignedInteger(&temp, sizeof(uint32_t)))
+  {
+    return false;
+  }
+
+  n = temp;
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(int8_t& n)
+{
+  int32_t temp;
+
+  if (!getSignedInteger(&temp, sizeof(int8_t)))
+  {
+    return false;
+  }
+
+  n = (int8_t)temp;
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(int16_t& n)
+{
+  int32_t temp;
+
+  if (!getSignedInteger(&temp, sizeof(int16_t)))
+  {
+    return false;
+  }
+
+  n = (uint16_t)temp;
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(int32_t& n)
+{
+  int32_t temp;
+
+  if (!getSignedInteger(&temp, sizeof(int32_t)))
+  {
+    return false;
+  }
+
+  n = temp;
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(float& n)
+{
+  /* Try to decode the next messagePack item as an 4-byte float */
+  uint32_t data;
+  uint8_t type;
+
+  /* Look at next type */
+  if (!peek(&type))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (type == _MP_FLOAT)
+  {
+    if (!remaining(sizeof(float)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 32-bit float */
+    data = read();
+    data = data << 8;
+    data |= read();
+    data = data << 8;
+    data |= read();
+    data = data << 8;
+    data |= read();
+
+    /* Convert to float */
+    memcpy(&n, &data, sizeof(float));
+
+    /* Success */
+    return true;
+  }
+
+  /* Can't convert this type */
+  _LOG_UNPACK_ERROR_TYPE;
+  return false;
+}
+
+bool BERGCloudMessageBase::unpack(bool& n)
+{
+  /* Try to decode the next messagePack item as boolean */
+  uint8_t type;
+
+  /* Look at next type */
+  if (!peek(&type))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if ((type == _MP_BOOL_FALSE) || (type == _MP_BOOL_TRUE))
+  {
+    n = (read() == _MP_BOOL_TRUE);
+
+    /* Success */
+    return true;
+  }
+
+  /* Can't convert this type */
+  _LOG_UNPACK_ERROR_TYPE;
+  return false;
+}
+
+bool BERGCloudMessageBase::unpack_nil(void)
+{
+  /* Try to decode the next messagePack item as nil */
+  uint8_t type;
+
+  /* Look at next type */
+  if (!peek(&type))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (type == _MP_NIL)
+  {
+    /* Read type */
+    read();
+
+    /* Success */
+    return true;
+  }
+
+  /* Can't convert this type */
+  _LOG_UNPACK_ERROR_TYPE;
+  return false;
+}
+
+bool BERGCloudMessageBase::unpack_array(uint16_t& items)
+{
+  /* Try to decode the next messagePack item as array */
+  uint8_t type;
+
+  /* Look at next type */
+  if (!peek(&type))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (IN_RANGE(type, _MP_FIXARRAY_MIN, _MP_FIXARRAY_MAX))
+  {
+    /* Read fix num value */
+    items = read() - _MP_FIXARRAY_MIN;
+
+    /* Success */
+    return true;
+  }
+
+  if (type == _MP_ARRAY16)
+  {
+    if (!remaining(sizeof(uint16_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 16-bit unsigned integer */
+    items = read();
+    items = items << 8;
+    items |= read();
+
+    /* Success */
+    return true;
+  }
+
+  if (type == _MP_ARRAY32)
+  {
+    /* Not yet supported */
+    _LOG_UNPACK_ERROR_TYPE;
+    return false;
+  }
+
+  /* Can't convert this type */
+  _LOG_UNPACK_ERROR_TYPE;
+  return false;
+}
+
+bool BERGCloudMessageBase::unpack_map(uint16_t& items)
+{
+  /* Try to decode the next messagePack item as array */
+  uint8_t type;
+
+  /* Look at next type */
+  if (!peek(&type))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (IN_RANGE(type, _MP_FIXMAP_MIN, _MP_FIXMAP_MAX))
+  {
+    /* Read fix num value */
+    items = read() - _MP_FIXMAP_MIN;
+
+    /* Success */
+    return true;
+  }
+
+  if (type == _MP_MAP16)
+  {
+    if (!remaining(sizeof(uint16_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 16-bit unsigned integer */
+    items = read();
+    items = items << 8;
+    items |= read();
+
+    /* Success */
+    return true;
+  }
+
+  if (type == _MP_MAP32)
+  {
+    /* Not yet supported */
+    _LOG_UNPACK_ERROR_TYPE;
+    return true;
+  }
+
+  /* Can't convert this type */
+  _LOG_UNPACK_ERROR_TYPE;
+  return false;
+}
+
+/* Separate header and data methods are provided for raw data*/
+/* so that Arduino strings may be unpacked without having to create */
+/* a temporary buffer first. */
+
+bool BERGCloudMessageBase::unpack_raw_header(uint16_t *sizeInBytes)
+{
+  uint8_t type;
+
+  /* Look at next type */
+  if (!peek(&type))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (IN_RANGE(type, _MP_FIXRAW_MIN, _MP_FIXRAW_MAX))
+  {
+    /* Read fix raw value */
+    *sizeInBytes = read() - _MP_FIXRAW_MIN;
+
+    /* Success */
+    return true;
+  }
+
+  if (type == _MP_RAW16)
+  {
+    if (!remaining(sizeof(uint16_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* Read 16-bit unsigned integer */
+    *sizeInBytes = read();
+    *sizeInBytes = *sizeInBytes << 8;
+    *sizeInBytes |= read();
+
+    /* Success */
+    return true;
+  }
+
+  if (type == _MP_RAW32)
+  {
+    if (!remaining(sizeof(uint32_t)))
+    {
+      _LOG_UNPACK_ERROR_NO_DATA;
+      return false;
+    }
+
+    /* Read type */
+    read();
+
+    /* read 32-bit unsigned integer */
+    *sizeInBytes = read();
+    *sizeInBytes = *sizeInBytes << 8;
+    *sizeInBytes |= read();
+    *sizeInBytes = *sizeInBytes << 8;
+    *sizeInBytes |= read();
+    *sizeInBytes = *sizeInBytes << 8;
+    *sizeInBytes |= read();
+
+    /* Success */
+    return true;
+  }
+
+  /* Can't convert this type */
+  _LOG_UNPACK_ERROR_TYPE;
+  return false;
+}
+
+bool BERGCloudMessageBase::unpack_raw_data(uint8_t *pData, uint16_t packedSizeInBytes, uint16_t bufferSizeInBytes)
+{
+  uint8_t data;
+
+  /* Unpack all bytes */
+  while (packedSizeInBytes-- > 0)
+  {
+    data = read();
+    
+    /* Only write up to the buffer size */
+    if (bufferSizeInBytes-- > 0)
+    {
+      *pData++ = data;
+    }
+  }
+
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(char *pString, uint32_t maxSizeInBytes)
+{
+  /* Try to decode a null-terminated C string */
+  uint16_t sizeInBytes;
+
+  if (!unpack_raw_header(&sizeInBytes))
+  {
+    return false;
+  }
+
+  if (!remaining(sizeInBytes))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (!unpack_raw_data((uint8_t *)pString, sizeInBytes, maxSizeInBytes - 1)) /* -1 to allow space for null terminator */
+  {
+    return false;
+  }
+
+  /* Add null terminator */
+  pString[sizeInBytes] = '\0';
+
+  /* Success */
+  return true;
+}
+
+bool BERGCloudMessageBase::unpack(uint8_t *pData, uint32_t maxSizeInBytes, uint32_t *pSizeInBytes)
+{
+  /* Try to decode a block of raw data */
+  uint16_t sizeInBytes;
+
+  if (!unpack_raw_header(&sizeInBytes))
+  {
+    return false;
+  }
+
+  if (!remaining(sizeInBytes))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  if (pSizeInBytes != NULL)
+  {
+    *pSizeInBytes = sizeInBytes;
+  }
+
+  return unpack_raw_data(pData, sizeInBytes, maxSizeInBytes);
+}
+
+bool BERGCloudMessageBase::unpack_find(const char *key)
+{
+  /* Search for a string key in a map; in this simple */
+  /* implementation maps cannot contain maps or arrays */
+  uint16_t last_read;
+  uint16_t map_items;
+  uint8_t type;
+  char keyString[MAX_MAP_KEY_STRING_LENGTH+1]; /* +1 for null terminator */
+
+  if (key == NULL)
+  {
+     return false;
+  }
+
+  if (strlen(key) > MAX_MAP_KEY_STRING_LENGTH)
+  {
+    _LOG("Unpack: Key name too long.\r\n");
+    return false;
+  }
+
+  /* Remember the current read position in the raw data */
+  last_read = bytesRead;
+
+  /* Start reading from the beginning of the data */
+  restart();
+
+  while(peek(&type))
+  {
+    if (IN_RANGE(type, _MP_FIXMAP_MIN, _MP_FIXMAP_MAX) || (type == _MP_MAP16)) /* _MP_MAP32 not yet supported */
+    {
+      /* Map found, get number of items */
+      unpack_map(map_items);
+
+      /* Iterate through the key-value pairs */
+      while (map_items-- > 0)
+      {
+        if (unpack(keyString, sizeof(keyString)))
+        {
+          /* String key found */
+          if (strcompare(keyString, key))
+          {
+              /* Match found */
+              return true;
+          }
+          else
+          {
+              /* No match, skip this value */
+              unpack_skip();
+          }
+        }
+        else
+        {
+          /* Not a suitable string key; skip this key and value */
+          unpack_skip();
+          unpack_skip();
+        }
+      }
+    }
+    else
+    {
+      /* Not a map */
+      unpack_skip();
+    }
+  }
+
+  /* Not found; return to last position */
+  bytesRead = last_read;
+  return false;
+}
+
+bool BERGCloudMessageBase::unpack_find(uint16_t i)
+{
+  /* Search for an index in an array; in this simple */
+  /* implementation arrays cannot contain maps or arrays */
+  uint16_t last_read;
+  uint16_t array_items;
+  uint16_t item;
+  uint8_t type;
+
+  /* Remember the current read position in the raw data */
+  last_read = bytesRead;
+
+  /* Start reading from the beginning of the data */
+  restart();
+
+  while(peek(&type))
+  {
+    if (IN_RANGE(type, _MP_FIXARRAY_MIN, _MP_FIXARRAY_MAX) || (type == _MP_ARRAY16)) /* _MP_ARRAY32 not yet supported */
+    {
+      /* Array found, get number of items */
+      unpack_array(array_items);
+
+      /* Assume items are numbered starting from one */
+      item = 1;
+
+      if (i == 0)
+      {
+        _LOG("Unpack: Array indexes start from 1.\r\n");
+        return false;
+      }
+
+      /* Iterate through the values in the array */
+      while (array_items-- > 0)
+      {
+        if (item++ == i)
+        {
+            /* Found it */
+            return true;
+        }
+        else
+        {
+          /* Skip this item */
+          unpack_skip();
+        }
+      }
+    }
+    else
+    {
+      /* Not an array */
+      unpack_skip();
+    }
+  }
+
+  /* Not found; return to last position */
+  bytesRead = last_read;
+  return false;
+}
+