BERG Cloud / BERGCloud

Dependents:   LittleCounter-Example

Revision:
0:b4ccb530b9eb
Child:
4:5a04c00b5b6f
--- /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)
+{
+}
+