BERG Cloud / BERGCloud

Dependents:   LittleCounter-Example

Files at this revision

API Documentation at this revision

Comitter:
nickludlam
Date:
Tue Nov 12 14:38:30 2013 +0000
Child:
1:93c28642660c
Commit message:
Initial commit

Changed in this revision

BERGCloud.h Show annotated file Show diff for this revision Revisions of this file
BERGCloudBase.cpp Show annotated file Show diff for this revision Revisions of this file
BERGCloudBase.h Show annotated file Show diff for this revision Revisions of this file
BERGCloudConfig.h Show annotated file Show diff for this revision Revisions of this file
BERGCloudConst.h Show annotated file Show diff for this revision Revisions of this file
BERGCloudLogPrint.h Show annotated file Show diff for this revision Revisions of this file
BERGCloudMbed.cpp Show annotated file Show diff for this revision Revisions of this file
BERGCloudMbed.h Show annotated file Show diff for this revision Revisions of this file
BERGCloudMessageBase.cpp Show annotated file Show diff for this revision Revisions of this file
BERGCloudMessageBase.h Show annotated file Show diff for this revision Revisions of this file
BERGCloudMessageBuffer.cpp Show annotated file Show diff for this revision Revisions of this file
BERGCloudMessageBuffer.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloud.h	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,37 @@
+/*
+
+BERGCloud library
+
+Copyright (c) 2013 BERG 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.
+
+*/
+
+#ifndef BERGCLOUD_H
+#define BERGCLOUD_H
+
+#ifdef ARDUINO
+#include "BERGCloudArduino.h"
+#else
+#error Please #include "BERGCloudMbed.h" or "BERGCloudLinux.h" instead.
+#endif
+
+#endif // #ifndef BERGCLOUD_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudBase.cpp	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,600 @@
+/*
+
+BERGCloud library common API
+
+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.
+
+*/
+
+
+#define __STDC_LIMIT_MACROS /* Include C99 stdint defines in C++ code */
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h> /* For memcpy() */
+
+#include "BERGCloudBase.h"
+
+#define SPI_POLL_TIMEOUT_MS 1000
+#define SPI_SYNC_TIMEOUT_MS 10000
+
+#define CONNECT_POLL_RATE_MS 250
+
+uint8_t BERGCloudBase::nullProductKey[BC_PRODUCT_KEY_SIZE_BYTES] = {0};
+
+bool BERGCloudBase::transaction(_BC_SPI_TRANSACTION *tr)
+{
+  uint16_t i, j;
+  uint8_t rxByte;
+  bool timeout;
+  uint8_t dataSize;
+  uint16_t groupSize;
+  uint16_t dataCRC;
+  uint16_t calcCRC;
+  uint8_t header[SPI_HEADER_SIZE_BYTES];
+  uint8_t footer[SPI_FOOTER_SIZE_BYTES];
+
+  /* Check synchronisation */
+  if (!synced)
+  {
+    timerReset();
+
+    do {
+      rxByte = SPITransaction(SPI_PROTOCOL_PAD, true);
+      timeout = timerRead_mS() > SPI_SYNC_TIMEOUT_MS;
+
+    } while ((rxByte != SPI_PROTOCOL_RESET) && !timeout);
+
+    if (timeout)
+    {
+      _LOG("Timeout, sync (BERGCloudBase::transaction)\r\n");
+      return false;
+    }
+
+    /* Resynchronisation successful */
+    synced = true;
+  }
+
+  /* Calculate total data size */
+  dataSize = 0;
+
+  for (i=0; i<_TX_GROUPS; i++)
+  {
+    dataSize += tr->tx[i].dataSize;
+  }
+
+  /* Initialise CRC */
+  calcCRC = 0xffff;
+
+  /* Create header */
+  header[0] = tr->command;
+  header[1] = 0x00; /* Reserved */
+  header[2] = 0x00; /* Reserved */
+  header[3] = dataSize;
+
+  /* Send header */
+  for (i=0; i<sizeof(header); i++)
+  {
+
+    calcCRC = Crc16(header[i], calcCRC);
+    rxByte = SPITransaction(header[i], false);
+
+    if (rxByte == SPI_PROTOCOL_RESET)
+    {
+      _LOG("Reset, send header (BERGCloudBase::transaction)\r\n");
+      return false;
+    }
+
+    if (rxByte != SPI_PROTOCOL_PAD)
+    {
+      _LOG("SyncErr, send header (BERGCloudBase::transaction)\r\n");
+      synced = false;
+      return false;
+    }
+  }
+
+  /* Send data groups */
+  for (i=0; i<_TX_GROUPS; i++)
+  {
+    for (j=0; j<tr->tx[i].dataSize; j++)
+    {
+      calcCRC = Crc16(tr->tx[i].buffer[j], calcCRC);
+      rxByte = SPITransaction(tr->tx[i].buffer[j], false);
+
+      if (rxByte == SPI_PROTOCOL_RESET)
+      {
+        _LOG("Reset, send data (BERGCloudBase::transaction)\r\n");
+        return false;
+      }
+
+      if (rxByte != SPI_PROTOCOL_PAD)
+      {
+        _LOG("SyncErr, send data (BERGCloudBase::transaction)\r\n");
+        synced = false;
+        return false;
+      }
+    }
+  }
+
+  /* Create footer */
+  footer[0] = calcCRC >> 8;
+  footer[1] = calcCRC & 0xff;
+
+  /* Send footer */
+  for (i=0; i<sizeof(footer); i++)
+  {
+    rxByte = SPITransaction(footer[i], false);
+
+    if (rxByte == SPI_PROTOCOL_RESET)
+    {
+      _LOG("Reset, send footer (BERGCloudBase::transaction)\r\n");
+      return false;
+    }
+
+    if (rxByte != SPI_PROTOCOL_PAD)
+    {
+      _LOG("SyncErr, send footer (BERGCloudBase::transaction)\r\n");
+      synced = false;
+      return false;
+    }
+  }
+
+  /* Poll for response */
+  timerReset();
+
+  do {
+    rxByte = SPITransaction(SPI_PROTOCOL_PAD, false);
+
+    if (rxByte == SPI_PROTOCOL_RESET)
+    {
+      _LOG("Reset, poll (BERGCloudBase::transaction)\r\n");
+      return false;
+    }
+
+    if (rxByte == SPI_PROTOCOL_PENDING)
+    {
+      /* Waiting for data; reset timeout */
+      timerReset();
+    }
+
+    timeout = timerRead_mS() > SPI_POLL_TIMEOUT_MS;
+
+  } while (((rxByte == SPI_PROTOCOL_PAD) || (rxByte == SPI_PROTOCOL_PENDING)) && !timeout);
+
+  if (timeout)
+  {
+    _LOG("Timeout, poll (BERGCloudBase::transaction)\r\n");
+    synced = false;
+    return false;
+  }
+
+  /* Initialise CRC */
+  calcCRC = 0xffff;
+
+  /* Read header, we already have the first byte */
+  header[0] = rxByte;
+  calcCRC = Crc16(header[0], calcCRC);
+
+  for (i=1; i < SPI_HEADER_SIZE_BYTES; i++)
+  {
+    header[i] = SPITransaction(SPI_PROTOCOL_PAD, false);
+    calcCRC = Crc16(header[i], calcCRC);
+  }
+
+  /* Get data size */
+  dataSize = header[3];
+
+  /* Read data groups */
+  for (i=0; i<_RX_GROUPS; i++)
+  {
+    groupSize = tr->rx[i].bufferSize;
+    j = 0; /* Start of the group buffer */
+
+    while((dataSize > 0) && (groupSize > 0))
+    {
+      tr->rx[i].buffer[j] = SPITransaction(SPI_PROTOCOL_PAD, false);
+      calcCRC = Crc16(tr->rx[i].buffer[j], calcCRC);
+
+      /* Next */
+      j++;
+
+      /* Update the total data remaining and the space remaining in this group. */
+      dataSize--;
+      groupSize--;
+    }
+
+    if (tr->rx[i].dataSize != NULL)
+    {
+      /* Return the number of bytes used in this buffer */
+      *tr->rx[i].dataSize = tr->rx[i].bufferSize - groupSize;
+    }
+  }
+
+  if (dataSize > 0)
+  {
+    /* Too much data sent */
+    _LOG("SizeErr, read data (BERGCloudBase::transaction)\r\n");
+    synced = false;
+    return false;
+  }
+
+  /* Read CRC; set nCS high */
+  dataCRC = SPITransaction(SPI_PROTOCOL_PAD, false); /* MSByte */
+  dataCRC <<= 8;
+  dataCRC |= SPITransaction(SPI_PROTOCOL_PAD, true /* nCS -> high */); /* LSByte */
+
+  /* Compare with calculated CRC */
+  if (calcCRC != dataCRC)
+  {
+    /* Invalid CRC */
+    _LOG("CRCErr, read data (BERGCloudBase::transaction)\r\n");
+    synced = false;
+    return false;
+  }
+
+  /* Get reponse code */
+  lastResponse = header[0];
+
+  return (lastResponse == SPI_RSP_SUCCESS);
+}
+
+void BERGCloudBase::initTransaction(_BC_SPI_TRANSACTION *tr)
+{
+  memset(tr, 0x00, sizeof(_BC_SPI_TRANSACTION));
+}
+
+bool BERGCloudBase::pollForCommand(uint8_t *commandBuffer, uint16_t commandBufferSize, uint16_t& commandSize, uint8_t& commandID)
+{
+  /* Returns TRUE if a valid command has been received */
+
+  _BC_SPI_TRANSACTION tr;
+  uint8_t cmdID[2] = {0};
+  uint16_t cmdIDSize = 0;
+
+  initTransaction(&tr);
+
+  tr.command = SPI_CMD_POLL_FOR_COMMAND;
+
+  tr.rx[0].buffer = cmdID;
+  tr.rx[0].bufferSize = sizeof(cmdID);
+  tr.rx[0].dataSize = &cmdIDSize;
+
+  tr.rx[1].buffer = commandBuffer;
+  tr.rx[1].bufferSize = commandBufferSize;
+  tr.rx[1].dataSize = &commandSize;
+
+  if (transaction(&tr))
+  {
+    commandID = cmdID[1];
+    return true;
+  }
+
+  commandID = 0;
+  commandSize = 0;
+  return false;
+}
+
+#ifdef BERGCLOUD_PACK_UNPACK
+bool BERGCloudBase::pollForCommand(BERGCloudMessageBuffer& buffer, uint8_t& commandID)
+{
+  /* Returns TRUE if a valid command has been received */
+
+  _BC_SPI_TRANSACTION tr;
+  uint8_t cmdID[2] = {0};
+  uint16_t cmdIDSize = 0;
+  uint16_t dataSize = 0;
+
+  initTransaction(&tr);
+  buffer.clear();
+
+  tr.command = SPI_CMD_POLL_FOR_COMMAND;
+
+  tr.rx[0].buffer = cmdID;
+  tr.rx[0].bufferSize = sizeof(cmdID);
+  tr.rx[0].dataSize = &cmdIDSize;
+
+  tr.rx[1].buffer = buffer.ptr();
+  tr.rx[1].bufferSize = buffer.size();
+  tr.rx[1].dataSize = &dataSize;
+
+  if (transaction(&tr))
+  {
+    commandID = cmdID[1];
+    buffer.used(dataSize);
+    return true;
+  }
+
+  commandID = 0;
+  buffer.used(0);
+  return false;
+}
+#endif
+
+bool BERGCloudBase::_sendEvent(uint8_t eventCode, uint8_t *eventBuffer, uint16_t eventSize, uint8_t command)
+{
+  /* Returns TRUE if the event is sent successfully */
+
+  _BC_SPI_TRANSACTION tr;
+  uint8_t header[4] = {0};
+
+  if (eventSize > (SPI_MAX_PAYLOAD_SIZE_BYTES - sizeof(header)))
+  {
+    /* Too big */
+    return false;
+  }
+
+  header[0] = eventCode;
+
+  initTransaction(&tr);
+
+  tr.command = command;
+  tr.tx[0].buffer = (uint8_t *)header;
+  tr.tx[0].dataSize = sizeof(header);
+  tr.tx[1].buffer = eventBuffer;
+  tr.tx[1].dataSize = (uint8_t)eventSize;
+
+  return transaction(&tr);
+}
+
+bool BERGCloudBase::sendEvent(uint8_t eventCode, uint8_t *eventBuffer, uint16_t eventSize)
+{
+  return _sendEvent(eventCode, eventBuffer, eventSize, SPI_CMD_SEND_EVENT_RAW);
+}
+
+#ifdef BERGCLOUD_PACK_UNPACK
+bool BERGCloudBase::sendEvent(uint8_t eventCode, BERGCloudMessageBuffer& buffer)
+{
+  bool result;
+
+  result = _sendEvent(eventCode, buffer.ptr(), buffer.used(), SPI_CMD_SEND_EVENT_PACKED);
+
+  buffer.clear();
+  return result;
+}
+#endif
+
+bool BERGCloudBase::getConnectionState(uint8_t& state)
+{
+  _BC_SPI_TRANSACTION tr;
+
+  initTransaction(&tr);
+
+  tr.command = SPI_CMD_GET_CONNECT_STATE;
+  tr.rx[0].buffer = &state;
+  tr.rx[0].bufferSize = sizeof(state);
+
+  return transaction(&tr);
+}
+
+bool BERGCloudBase::getSignalQuality(int8_t& rssi, uint8_t& lqi)
+{
+  _BC_SPI_TRANSACTION tr;
+
+  initTransaction(&tr);
+
+  tr.command = SPI_CMD_GET_SIGNAL_QUALITY;
+  tr.rx[0].buffer = (uint8_t *)&rssi;
+  tr.rx[0].bufferSize = sizeof(rssi);
+  tr.rx[1].buffer = &lqi;
+  tr.rx[1].bufferSize = sizeof(lqi);
+
+  return transaction(&tr);
+}
+
+bool BERGCloudBase::connect(const uint8_t (&productKey)[BC_PRODUCT_KEY_SIZE_BYTES], uint16_t version, bool waitForConnected)
+{
+  _BC_SPI_TRANSACTION tr;
+  uint16_t hostType = BC_HOST_UNKNOWN;
+  uint8_t connectData [sizeof(version) + sizeof(hostType)];
+
+  uint8_t lastState = BC_CONNECT_STATE_DISCONNECTED;
+  uint8_t state;
+
+#ifndef BERGCLOUD_NO_HOST_TYPE
+  /* Get host type */
+  hostType = getHostType();
+#endif
+
+  initTransaction(&tr);
+
+  connectData[0] = hostType;
+  connectData[1] = hostType >> 8;
+  connectData[2] = version;
+  connectData[3] = version >> 8;
+
+  tr.command = SPI_CMD_SEND_PRODUCT_ANNOUNCE;
+  tr.tx[0].buffer = (uint8_t *)productKey;
+  tr.tx[0].dataSize = sizeof(productKey);
+  tr.tx[1].buffer = connectData;
+  tr.tx[1].dataSize = sizeof(connectData);
+
+  if (!transaction(&tr))
+  {
+    return false;
+  }
+
+  if (waitForConnected)
+  {
+    /* Poll until connected */
+    do {
+      timerReset();
+      while (timerRead_mS() < CONNECT_POLL_RATE_MS);
+
+      if (!getConnectionState(state))
+      {
+        return false;
+      }
+
+      if (state != lastState)
+      {
+        switch (state)
+        {
+          case BC_CONNECT_STATE_CONNECTED:
+            _LOG("connect: Connected\r\n");
+            break;
+          case BC_CONNECT_STATE_CONNECTING:
+            _LOG("connect: Connecting...\r\n");
+            break;
+          default:
+          case BC_CONNECT_STATE_DISCONNECTED:
+            _LOG("connect: Disconnected\r\n");
+            break;
+        }
+
+        lastState = state;
+      }
+
+    } while (state != BC_CONNECT_STATE_CONNECTED);
+  }
+
+  return true;
+}
+
+bool BERGCloudBase::getClaimingState(uint8_t& state)
+{
+  _BC_SPI_TRANSACTION tr;
+
+  initTransaction(&tr);
+
+  tr.command = SPI_CMD_GET_CLAIM_STATE;
+  tr.rx[0].buffer = &state;
+  tr.rx[0].bufferSize = sizeof(state);
+
+  return transaction(&tr);
+}
+
+bool BERGCloudBase::getClaimcode(const char (&claimcode)[BC_CLAIMCODE_SIZE_BYTES])
+{
+  _BC_SPI_TRANSACTION tr;
+
+  initTransaction(&tr);
+
+  tr.command = SPI_CMD_GET_CLAIMCODE;
+  tr.rx[0].buffer = (uint8_t *)claimcode;
+  tr.rx[0].bufferSize = sizeof(claimcode);
+
+  return transaction(&tr);
+}
+
+bool BERGCloudBase::getEUI64(uint8_t type, uint8_t (&eui64)[BC_EUI64_SIZE_BYTES])
+{
+  _BC_SPI_TRANSACTION tr;
+
+  initTransaction(&tr);
+
+  tr.command = SPI_CMD_GET_EUI64;
+  tr.tx[0].buffer = &type;
+  tr.tx[0].dataSize = sizeof(uint8_t);
+  tr.rx[0].buffer = eui64;
+  tr.rx[0].bufferSize = sizeof(eui64);
+
+  return transaction(&tr);
+}
+
+bool BERGCloudBase::getDeviceAddress(uint8_t (&address)[BC_ADDRESS_SIZE_BYTES])
+{
+  _BC_SPI_TRANSACTION tr;
+
+  initTransaction(&tr);
+
+  tr.command = SPI_CMD_GET_ADDRESS;
+  tr.rx[0].buffer = address;
+  tr.rx[0].bufferSize = sizeof(address);
+
+  return transaction(&tr);
+}
+
+bool BERGCloudBase::setDisplayStyle(uint8_t style)
+{
+  _BC_SPI_TRANSACTION tr;
+
+  initTransaction(&tr);
+
+  tr.command = SPI_CMD_SET_DISPLAY_STYLE;
+  tr.tx[0].buffer = &style;
+  tr.tx[0].dataSize = sizeof(style);
+
+  return transaction(&tr);
+}
+
+bool BERGCloudBase::clearDisplay(void)
+{
+  return setDisplayStyle(BC_DISPLAY_CLEAR);
+}
+
+bool BERGCloudBase::display(const char *text)
+{
+  _BC_SPI_TRANSACTION tr;
+  uint8_t strLen = 0;
+  const char *tmp = text;
+
+  if (text == NULL)
+  {
+    return false;
+  }
+
+  initTransaction(&tr);
+
+  /* Get string length excluding terminator */
+  while ((*tmp++ != '\0') && (strLen < UINT8_MAX))
+  {
+    strLen++;
+  }
+
+  tr.command = SPI_CMD_DISPLAY_PRINT;
+  tr.tx[0].buffer = (uint8_t *)text;
+  tr.tx[0].dataSize = strLen;
+
+  return transaction(&tr);
+}
+
+uint16_t BERGCloudBase::Crc16(uint8_t data, uint16_t crc)
+{
+  /* From Ember's code */
+  crc = (crc >> 8) | (crc << 8);
+  crc ^= data;
+  crc ^= (crc & 0xff) >> 4;
+  crc ^= (crc << 8) << 4;
+
+  crc ^= ( (uint8_t) ( (uint8_t) ( (uint8_t) (crc & 0xff) ) << 5)) |
+    ((uint16_t) ( (uint8_t) ( (uint8_t) (crc & 0xff)) >> 3) << 8);
+
+  return crc;
+}
+
+uint8_t BERGCloudBase::SPITransaction(uint8_t dataOut, bool finalCS)
+{
+  uint8_t dataIn = 0;
+
+  SPITransaction(&dataOut, &dataIn, (uint16_t)1, finalCS);
+
+  return dataIn;
+}
+
+void BERGCloudBase::begin(void)
+{
+  synced = false;
+  lastResponse = SPI_RSP_SUCCESS;
+}
+
+void BERGCloudBase::end(void)
+{
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudBase.h	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,116 @@
+/*
+
+BERGCloud library common API
+
+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.
+
+*/
+
+
+#ifndef BERGCLOUDBASE_H
+#define BERGCLOUDBASE_H
+
+#include "BERGCloudConfig.h"
+#include "BERGCloudConst.h"
+#include "BERGCloudLogPrint.h"
+
+#ifdef BERGCLOUD_PACK_UNPACK
+#include "BERGCloudMessageBuffer.h"
+#endif
+
+#define BERGCLOUD_LIB_VERSION (0x0100)
+
+#define _TX_GROUPS (2)
+#define _RX_GROUPS (2)
+
+typedef struct {
+  uint8_t *buffer;
+  uint16_t dataSize;
+} _BC_TX_GROUP;
+
+typedef struct {
+  uint8_t *buffer;
+  uint16_t bufferSize;
+  uint16_t *dataSize;
+} _BC_RX_GROUP;
+
+typedef struct {
+  uint8_t command;
+  _BC_TX_GROUP tx[_TX_GROUPS];
+  _BC_RX_GROUP rx[_RX_GROUPS];
+} _BC_SPI_TRANSACTION;
+
+class BERGCloudBase
+{
+public:
+  /* Check for a command */
+  bool pollForCommand(uint8_t *commandBuffer, uint16_t commandBufferSize, uint16_t& commandSize, uint8_t& commandID);
+#ifdef BERGCLOUD_PACK_UNPACK
+  bool pollForCommand(BERGCloudMessageBuffer& buffer, uint8_t& commandID);
+#endif
+  /* Send an event */
+  bool sendEvent(uint8_t eventCode, uint8_t *eventBuffer, uint16_t eventSize);
+#ifdef BERGCLOUD_PACK_UNPACK
+  bool sendEvent(uint8_t eventCode, BERGCloudMessageBuffer& buffer);
+#endif
+  /* Get the connection state */
+  bool getConnectionState(uint8_t& state);
+  /* Get the last-hop signal quality */
+  bool getSignalQuality(int8_t& rssi, uint8_t& lqi);
+  /* Connect */
+  bool connect(const uint8_t (&productKey)[BC_PRODUCT_KEY_SIZE_BYTES] = nullProductKey, uint16_t version = 0, bool waitForConnected = false);
+  /* Check if the device has been claimed */
+  bool getClaimingState(uint8_t& state);
+  /* Get the current claimcode */
+  bool getClaimcode(const char (&claimcode)[BC_CLAIMCODE_SIZE_BYTES]);
+  /* Get the EUI64 identifier for this node, its parent or the network coordinator */
+  bool getEUI64(uint8_t type, uint8_t (&eui64)[BC_EUI64_SIZE_BYTES]);
+  /* Get the Device Address */
+  bool getDeviceAddress(uint8_t (&address)[BC_ADDRESS_SIZE_BYTES]);
+  /* Set the display style for the OLED display. This also clears the display. */
+  bool setDisplayStyle(uint8_t style);
+  /* Clear the OLED display */
+  bool clearDisplay(void);
+  /* Display a line of text on the OLED display */
+  bool display(const char *text);
+
+  /* Internal methods */
+public:
+  uint8_t lastResponse;
+  static uint8_t nullProductKey[BC_PRODUCT_KEY_SIZE_BYTES];
+protected:
+  void begin(void);
+  void end(void);
+  uint16_t Crc16(uint8_t data, uint16_t crc);
+  virtual uint16_t SPITransaction(uint8_t *dataOut, uint8_t *dataIn, uint16_t dataSize, bool finalCS) = 0;
+  virtual void timerReset(void) = 0;
+  virtual uint32_t timerRead_mS(void) = 0;
+  virtual uint16_t getHostType(void) = 0;
+private:
+  uint8_t SPITransaction(uint8_t data, bool finalCS);
+  void initTransaction(_BC_SPI_TRANSACTION *tr);
+  bool transaction(_BC_SPI_TRANSACTION *tr);
+  bool _sendEvent(uint8_t eventCode, uint8_t *eventBuffer, uint16_t eventSize, uint8_t command);
+  bool synced;
+};
+
+#endif // #ifndef BERGCLOUDBASE_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudConfig.h	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,40 @@
+/*
+
+BERGCloud compile-time configuration options
+
+Copyright (c) 2013 BERG 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.
+
+*/
+
+
+#ifndef BERGCLOUDCONFIG_H
+#define BERGCLOUDCONFIG_H
+
+/* Include debug logging */
+#define BERGCLOUD_LOG
+
+/* Include pack/unpack */
+#ifndef LINUX
+#define BERGCLOUD_PACK_UNPACK
+#endif
+
+#endif // #ifndef BERGCLOUDCONFIG_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudConst.h	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,129 @@
+/*
+
+BERGCloud constant definitions
+
+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.
+
+*/
+
+/*
+ * Sizes of things
+ */
+
+#define BC_EUI64_SIZE_BYTES            8
+#define BC_ADDRESS_SIZE_BYTES          8
+#define BC_CLAIMCODE_SIZE_BYTES        20
+#define BC_PRODUCT_KEY_SIZE_BYTES      16
+#define BC_PRINT_MAX_CHARS             26
+
+/*
+ * Network commands
+ */
+
+#define BC_EVENT_PRODUCT_ANNOUNCE      0xA000
+#define BC_COMMAND_SET_ADDRESS         0xB000
+
+#define BC_COMMAND_START_RAW           0xC000
+#define BC_COMMAND_START_PACKED        0xC100
+#define BC_COMMAND_ID_MASK             0x00FF
+#define BC_COMMAND_FORMAT_MASK         0xFF00
+
+#define BC_COMMAND_DISPLAY_IMAGE       0xD000
+#define BC_COMMAND_DISPLAY_TEXT        0xD001
+
+#define BC_EVENT_START_RAW             0xE000
+#define BC_EVENT_START_PACKED          0xE100
+#define BC_EVENT_ID_MASK               0x00FF
+#define BC_EVENT_FORMAT_MASK           0xFF00
+
+#define BC_COMMAND_FIRMWARE_ARDUINO    0xF010
+#define BC_COMMAND_FIRMWARE_MBED       0xF020
+
+/*
+ * SPI bus commands
+ */
+
+#define SPI_CMD_GET_CONNECT_STATE      0x80
+#define SPI_CMD_GET_CLAIMCODE          0x81
+#define SPI_CMD_GET_CLAIM_STATE        0x82
+#define SPI_CMD_GET_SIGNAL_QUALITY     0x83
+#define SPI_CMD_GET_EUI64              0x90
+
+#define SPI_CMD_SEND_PRODUCT_ANNOUNCE  0xA0
+#define SPI_CMD_GET_ADDRESS            0xB0
+#define SPI_CMD_POLL_FOR_COMMAND       0xC0
+#define SPI_CMD_SET_DISPLAY_STYLE      0xD0
+#define SPI_CMD_DISPLAY_PRINT          0xD1
+#define SPI_CMD_SEND_EVENT_RAW         0xE0
+#define SPI_CMD_SEND_EVENT_PACKED      0xE1
+
+#define SPI_PROTOCOL_PAD               0xff
+#define SPI_PROTOCOL_PENDING           0xfa
+#define SPI_PROTOCOL_RESET             0xf5
+
+/* For SPI_CMD_GET_CONNECT_STATE */
+#define BC_CONNECT_STATE_CONNECTED     0x00
+#define BC_CONNECT_STATE_CONNECTING    0x01
+#define BC_CONNECT_STATE_DISCONNECTED  0x02
+
+/* For SPI_CMD_GET_EUI64 */
+#define BC_EUI64_NODE                  0x00
+#define BC_EUI64_PARENT                0x01
+#define BC_EUI64_COORDINATOR           0x02
+
+/* For SPI_CMD_SEND_PRODUCT_ANNOUNCE */
+#define BC_HOST_UNKNOWN                0x0000
+#define BC_HOST_ARDUINO                0x1000
+#define BC_HOST_MBED                   0x2000
+#define BC_HOST_LINUX                  0x3000
+
+/* For SPI_CMD_GET_CLAIM_STATE */
+#define BC_CLAIM_STATE_CLAIMED         0x00
+#define BC_CLAIM_STATE_NOT_CLAIMED     0x01
+
+/* For SPI_CMD_DISPLAY_STYLE */
+#define BC_DISPLAY_NONE                0x00
+#define BC_DISPLAY_STYLE_ONE_LINE      0x01
+#define BC_DISPLAY_STYLE_TWO_LINES     0x02
+#define BC_DISPLAY_STYLE_FOUR_LINES    0x04
+
+#define BC_DISPLAY_CLEAR               0xc0
+/* Clear the display without changing the style */
+
+/* For SPI_CMD_SEND_EVENT_ */
+#define SPI_EVENT_HEADER_SIZE_BYTES    4
+
+/* SPI data sizes */
+#define SPI_MAX_PACKET_SIZE_BYTES      128
+#define SPI_HEADER_SIZE_BYTES          4
+#define SPI_FOOTER_SIZE_BYTES          2
+
+#define SPI_MAX_PAYLOAD_SIZE_BYTES \
+(SPI_MAX_PACKET_SIZE_BYTES - (SPI_HEADER_SIZE_BYTES + SPI_FOOTER_SIZE_BYTES))
+
+/* Response status values */
+#define SPI_RSP_SUCCESS                0x00
+#define SPI_RSP_INVALID_COMMAND        0x01
+#define SPI_RSP_BUSY                   0x02
+#define SPI_RSP_NO_DATA                0x03
+#define SPI_RSP_SEND_FAILED            0x04
+#define SPI_RSP_NO_FREE_BUFFERS        0x05
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudLogPrint.h	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,51 @@
+/*
+
+Debug logging helpers
+
+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.
+
+*/
+
+#ifndef BERGCLOUDLOGPRINT_H
+#define BERGCLOUDLOGPRINT_H
+
+#ifdef BERGCLOUD_LOG
+#ifdef ARDUINO
+#include <Arduino.h>
+// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
+// Credit: http://forum.arduino.cc/index.php/topic,85840.0.html
+#ifdef PROGMEM
+#undef PROGMEM
+#define PROGMEM __attribute__((section(".progmem.data")))
+#endif // #ifdef PROGMEM
+#define _LOG(x) Serial.print(F(x))
+#define _LOG_HEX(x) if ((x) < 0x10) Serial.print(F("0")); Serial.print((x), HEX)
+#else // #ifdef ARDUINO
+#include <stdio.h>
+#define _LOG(x) printf(x)
+#define _LOG_HEX(x) printf("%02X", (x))
+#endif // #ifdef ARDUINO
+#else // #ifdef BERGCLOUD_LOG
+#define _LOG(x)
+#endif // #ifdef BERGCLOUD_LOG
+
+#endif // #ifndef BERGCLOUDLOGPRINT_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudMbed.cpp	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,173 @@
+/*
+
+BERGCloud library for mbed
+
+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 <cstdint>
+#include <cstddef>
+#include <cstdarg>
+
+#include "BERGCloudMbed.h"
+
+uint16_t BERGCloudMbed::SPITransaction(uint8_t *dataOut, uint8_t *dataIn, uint16_t dataSize, bool finalCS)
+{
+  uint16_t i;
+
+  if ( (dataOut == NULL) || (dataIn == NULL) || (spi == NULL) )
+  {
+    _LOG("Invalid parameter (CBERGCloud::SPITransaction)\r\n");
+    return 0;
+  }
+
+  nSSELPin->write(0);
+
+  for (i = 0; i < dataSize; i++)
+  {
+    *dataIn++ = spi->write(*dataOut++);
+  }
+
+  if (finalCS)
+  {
+    nSSELPin->write(1);
+  }
+
+  return dataSize;
+}
+
+void BERGCloudMbed::timerReset(void)
+{
+  timer->reset();
+}
+
+uint32_t BERGCloudMbed::timerRead_mS(void)
+{
+  return timer->read_ms();
+}
+
+void BERGCloudMbed::begin(PinName _MOSIPin, PinName _MISOPin, PinName _SCLKPin, PinName _nSSELPin)
+{
+  /* Call base class method */
+  BERGCloudBase::begin();
+
+  /* Configure nSSEL control pin */
+  nSSELPin = new DigitalOut(_nSSELPin);
+
+  if (nSSELPin == NULL)
+  {
+    _LOG("nSSELPin is NULL (CBERGCloud::begin)\r\n");
+    return;
+  }
+
+  nSSELPin->write(1);
+
+  /* Configure SPI */
+  spi = new SPI(_MOSIPin, _MISOPin, _SCLKPin);
+
+  if (spi  == NULL)
+  {
+    _LOG("spi is NULL (CBERGCloud::begin)\r\n");
+    delete nSSELPin;
+    return;
+  }
+
+  spi->format(8, 0); /* 8-bits; SPI MODE 0 */
+  spi->frequency(4000000); /* 4MHz */
+
+  /* Configure timer */
+  timer = new Timer();
+
+  if (timer  == NULL)
+  {
+    _LOG("timer is NULL (CBERGCloud::begin)\r\n");
+    delete nSSELPin;
+    delete spi;
+    return;
+  }
+
+  timer->start();
+}
+
+void BERGCloudMbed::end()
+{
+  if (nSSELPin != NULL)
+  {
+    delete nSSELPin;
+  }
+
+  if (spi != NULL)
+  {
+    delete spi;
+  }
+
+  if (timer != NULL)
+  {
+    delete timer;
+  }
+
+  /* Call base class method */
+  BERGCloudBase::end();
+}
+
+bool BERGCloudMbed::display(std::string& s)
+{
+  return display(s.c_str());
+}
+
+uint16_t BERGCloudMbed::getHostType(void)
+{
+  return BC_HOST_MBED;
+}
+
+#ifdef BERGCLOUD_PACK_UNPACK
+
+bool BERGCloudMessage::pack(std::string& s)
+{
+  return pack(s.c_str());
+}
+
+bool BERGCloudMessage::unpack(std::string& s)
+{
+  uint16_t sizeInBytes;
+
+  if (!unpack_raw_header(&sizeInBytes))
+  {
+    return false;
+  }
+
+  if (!remaining(sizeInBytes))
+  {
+    _LOG_UNPACK_ERROR_NO_DATA;
+    return false;
+  }
+
+  std::string tmp(buffer[bytesRead], sizeInBytes);
+  bytesRead += sizeInBytes;
+
+  s = tmp;
+
+  return true;
+}
+
+#endif // #ifdef BERGCLOUD_PACK_UNPACK
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudMbed.h	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,71 @@
+/*
+
+BERGCloud library for mbed
+
+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.
+
+*/
+
+#ifndef BERGCLOUDMBED_H
+#define BERGCLOUDMBED_H
+
+#include "mbed.h"
+#include <string>
+#include "BERGCloudBase.h"
+
+#ifdef BERGCLOUD_PACK_UNPACK
+#include "BERGCloudMessageBase.h"
+#endif
+
+class BERGCloudMbed : public BERGCloudBase
+{
+public:
+  void begin(PinName _MOSIPin, PinName _MISOPin, PinName _SCLKPin, PinName _nSSELPin);
+  void end();
+  using BERGCloudBase::display;
+  /* Methods using std::string class */
+  bool display(std::string& s);
+private:
+  uint16_t SPITransaction(uint8_t *dataOut, uint8_t *dataIn, uint16_t dataSize, bool finalCS);
+  void timerReset(void);
+  uint32_t timerRead_mS(void);
+  uint16_t getHostType(void);
+  SPI *spi;
+  DigitalOut *nSSELPin;
+  Timer *timer;
+};
+
+#ifdef BERGCLOUD_PACK_UNPACK
+
+class BERGCloudMessage : public BERGCloudMessageBase
+{
+public:
+  using BERGCloudMessageBase::pack;
+  using BERGCloudMessageBase::unpack;
+  /* Methods using std::string class */
+  bool pack(std::string& s);
+  bool unpack(std::string& s);
+};
+
+#endif // #ifdef BERGCLOUD_PACK_UNPACK
+
+#endif // #ifndef BERGCLOUDMBED_H
+
--- /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;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudMessageBase.h	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,148 @@
+/*
+
+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.
+
+*/
+
+#ifndef BERGCLOUDMESSAGEBASE_H
+#define BERGCLOUDMESSAGEBASE_H
+
+#include "BERGCloudConfig.h"
+#include "BERGCloudMessageBuffer.h"
+#include "BERGCloudLogPrint.h"
+
+#define _LOG_PACK_ERROR_NO_SPACE    _LOG("Pack: Out of space.\r\n")
+#define _LOG_UNPACK_ERROR_TYPE      _LOG("Unpack: Can't convert to this variable type.\r\n")
+#define _LOG_UNPACK_ERROR_NO_DATA   _LOG("Unpack: No more data.\r\n")
+
+#define IN_RANGE(value, min, max) ((value >= min) && (value <= max))
+
+#define MAX_MAP_KEY_STRING_LENGTH (16)
+
+class BERGCloudMessageBase : public BERGCloudMessageBuffer
+{
+public:
+  BERGCloudMessageBase(void);
+  ~BERGCloudMessageBase(void);
+
+  /* Remove all items */
+  void clear(void);
+
+  /*
+   *  Pack methods
+   */
+
+  /* Pack an unsigned integer */
+  bool pack(uint8_t n);
+  bool pack(uint16_t n);
+  bool pack(uint32_t n);
+  /* Pack a signed integer */
+  bool pack(int8_t n);
+  bool pack(int16_t n);
+  bool pack(int32_t n);
+  /* Pack a float */
+  bool pack(float n);
+  /* Pack a boolean */
+  bool pack(bool n);
+
+  /* Pack a nil type */
+  bool pack_nil(void);
+  /* Pack an array header, giving the number of items that will follow */
+  bool pack_array(uint16_t items);
+  /* Pack a map header, giving the number of key-value pairs that will follow */
+  bool pack_map(uint16_t items);
+
+  /* Pack an array of data */
+  bool pack(uint8_t *data, uint16_t sizeInBytes);
+  /* Pack a null-terminated C string */
+  bool pack(const char *string);
+
+  /*
+   *  Unpack methods
+   */
+
+  /* Unpack an unsigned integer */
+  bool unpack(uint8_t& n);
+  bool unpack(uint16_t& n);
+  bool unpack(uint32_t& n);
+  /* Unpack a signed integer */
+  bool unpack(int8_t& n);
+  bool unpack(int16_t& n);
+  bool unpack(int32_t& n);
+  /* Unpack a float */
+  bool unpack(float& n);
+  /* Unpack a boolean */
+  bool unpack(bool& n);
+
+  /* Unpack a nil type */
+  bool unpack_nil(void);
+
+  /* Unpack an array header, this gives the number of items that follow */
+  bool unpack_array(uint16_t& items);
+  /* Unpack a map header, this gives the number of key-value pairs that follow */
+  bool unpack_map(uint16_t& items);
+
+  /* Get the messagePack type of the next item without unpacking it, */
+  /* returns false if there are no more items */
+  bool unpack_peek(uint8_t& messagePackType);
+
+#ifdef BERGCLOUD_LOG
+  /* Prints the type of the next item without unpacking it, */
+  /* returns false if there are no more items */
+  bool unpack_peek(void);
+  /* Prints the type of all items without unpacking them */
+  void print(void);
+  /* Print the raw MessagePack bytes of a message */
+  void print_bytes(void);
+#endif
+
+  /* Skip the next item */
+  bool unpack_skip(void);
+  /* Restart unpacking from the beginning */
+  bool unpack_restart();
+  /* Moves to the value associated with the map key 'key' */
+  bool unpack_find(const char *key);
+  /* Moves to the value associated with array index 'i' */
+  bool unpack_find(uint16_t i);
+
+  /* Unpack a null-terminated C string */
+  bool unpack(char *string, uint32_t maxSizeInBytes);
+  /* Unpack an array of data */
+  bool unpack(uint8_t *data, uint32_t maxSizeInBytes, uint32_t *sizeInBytes = NULL);
+
+protected:
+  /* Internal methods */
+  uint16_t strlen(const char *string);
+  bool strcompare(const char *s1, const char *s2);
+  bool pack_raw_header(uint16_t sizeInBytes);
+  bool pack_raw_data(uint8_t *data, uint16_t sizeInBytes);
+  bool unpack_raw_header(uint16_t *sizeInBytes);
+  bool unpack_raw_data(uint8_t *data, uint16_t packedSizeInBytes, uint16_t bufferSizeInBytes);
+  bool getUnsignedInteger(uint32_t *value, uint8_t maxBytes);
+  bool getSignedInteger(int32_t *value, uint8_t maxBytes);
+};
+
+#endif // #ifndef BERGCLOUDMESSAGEBASE_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudMessageBuffer.cpp	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,118 @@
+/*
+
+Simple message buffer
+
+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 "BERGCloudMessageBuffer.h"
+
+BERGCloudMessageBuffer::BERGCloudMessageBuffer(void)
+{
+  clear();
+}
+
+void BERGCloudMessageBuffer::clear(void)
+{
+  bytesWritten = 0; /* Number of bytes written */
+  bytesRead = 0;    /* Number of bytes read */
+}
+
+void BERGCloudMessageBuffer::restart(void)
+{
+  /* Restart reading from the beginning */
+  bytesRead = 0;
+}
+
+uint16_t BERGCloudMessageBuffer::size(void)
+{
+  /* Get total size of the buffer */
+  return BUFFER_SIZE_BYTES;
+}
+
+uint16_t BERGCloudMessageBuffer::used(void)
+{
+  /* Get mumber of bytes used in the buffer */
+  return bytesWritten;
+}
+
+void BERGCloudMessageBuffer::used(uint16_t used)
+{
+  /* Set number of bytes used in the buffer */
+  bytesWritten = used;
+}
+
+uint16_t BERGCloudMessageBuffer::available(void)
+{
+  /* Get space available in the buffer */
+  return BUFFER_SIZE_BYTES - bytesWritten;
+}
+
+bool BERGCloudMessageBuffer::available(uint16_t required)
+{
+  /* Test if space is available for the number of bytes required */
+  return (BUFFER_SIZE_BYTES - bytesWritten) >= required;
+}
+
+void BERGCloudMessageBuffer::add(uint8_t data)
+{
+  /* Write a byte to the buffer; no checks */
+    buffer[bytesWritten++] = data;
+}
+
+bool BERGCloudMessageBuffer::peek(uint8_t *data)
+{
+  /* Peek at the next byte in the buffer */
+  if (bytesRead >= bytesWritten)
+  {
+    /* No more data */
+    return false;
+  }
+
+  
+  *data = buffer[bytesRead]; /* No increment */
+  return true;
+}
+
+uint8_t BERGCloudMessageBuffer::read(void)
+{
+  /* Read the next byte from the buffer; no checks */
+  return buffer[bytesRead++];
+}
+
+uint16_t BERGCloudMessageBuffer::remaining(void)
+{
+  /* Returns the number of bytes that have been written but not read */
+  return bytesWritten - bytesRead;
+}
+
+bool BERGCloudMessageBuffer::remaining(uint16_t required)
+{
+  /* Test if data is remaining for the number of bytes required */
+  return (bytesWritten - bytesRead) >= required;
+}
+
+uint8_t *BERGCloudMessageBuffer::ptr(void)
+{
+  return buffer;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BERGCloudMessageBuffer.h	Tue Nov 12 14:38:30 2013 +0000
@@ -0,0 +1,67 @@
+/*
+
+Simple message buffer
+
+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.
+
+*/
+
+#ifndef BERGCLOUDMESSAGEBUFFER_H
+#define BERGCLOUDMESSAGEBUFFER_H
+
+#define __STDC_LIMIT_MACROS /* Include C99 stdint defines in C++ code */
+#include <stdint.h>
+
+/* Default buffer size */
+#ifndef BUFFER_SIZE_BYTES
+#define BUFFER_SIZE_BYTES 64
+#endif
+
+class BERGCloudMessageBuffer
+{
+public:
+  BERGCloudMessageBuffer();
+  uint16_t size(void);
+  uint8_t *ptr(void);
+  void clear(void);
+
+  /* Methods for writing to the buffer */
+  uint16_t used(void);
+  void used(uint16_t used);
+  uint16_t available(void);
+  bool available(uint16_t required);
+  void add(uint8_t data);
+
+  /* Methods for reading from the buffer */
+  bool peek(uint8_t *data);
+  uint8_t read(void);
+  uint16_t remaining(void);
+  bool remaining(uint16_t required);
+  void restart(void);
+
+protected:
+  uint8_t buffer[BUFFER_SIZE_BYTES];
+  uint16_t bytesWritten;
+  uint16_t bytesRead;
+};
+
+#endif // #ifndef BERGCLOUDMESSAGEBUFFER_H
+