added debugging

Fork of BLE_nRF8001 by RedBearLab

BLE_nRF8001/nRF8001.cpp

Committer:
jn80842
Date:
2014-11-10
Revision:
2:7805a5595aab
Parent:
0:075ea2812998

File content as of revision 2:7805a5595aab:


#include "BLEAttribute.h"
#include "BLEService.h"
#include "BLECharacteristic.h"
#include "BLEDescriptor.h"
#include "BLEUuid.h"

#include "nRF8001.h"

//#define NRF_8001_DEBUG

#define ADVERTISING_INTERVAL 0x050

struct setupMsgData {
  unsigned char length;
  unsigned char cmd;
  unsigned char type;
  unsigned char offset;
  unsigned char data[28];
};

#define NB_BASE_SETUP_MESSAGES 7

/* Store the setup for the nRF8001 in the flash of the AVR to save on RAM */
static hal_aci_data_t baseSetupMsgs[NB_BASE_SETUP_MESSAGES] PROGMEM = {\
  {0x00,\
    {\
      0x07,0x06,0x00,0x00,0x03,0x02,0x41,0xfe,\
    },\
  },\
  {0x00,\
    {\
      0x1f,0x06,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x06,0x00,0x06,\
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
    },\
  },\
  {0x00,\
    {\
      0x1f,0x06,0x10,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x90,0x00,0xff,\
    },\
  },\
  {0x00,\
    {\
      0x1f,0x06,0x10,0x38,0xff,0xff,0x02,0x58,0x0a,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x00,0x00,\
    },\
  },\
  {0x00,\
    {\
      0x05,0x06,0x10,0x54,0x00,0x02,\
    },\
  },\
  {0x00,\
    {\
      0x19,0x06,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
      0x00,0x00,0x00,0x00,0x00,0x00,\
    },\
  },\
  {0x00,\
    {\
      0x19,0x06,0x70,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
      0x00,0x00,0x00,0x00,0x00,0x00,\
    },\
  },\
};

void __ble_assert(const char *file, uint16_t line)
{
  serial.printf("ERROR ");
  serial.printf(file);
  serial.printf(": ");
  serial.printf("%d\n",line);
  while(1);
}

/** crc function to re-calulate the CRC after making changes to the setup data.
*/
uint16_t crc_16_ccitt(uint16_t crc, uint8_t * data_in, uint16_t data_len) {

  uint16_t i;

  for(i = 0; i < data_len; i++)
  {
    crc  = (unsigned char)(crc >> 8) | (crc << 8);
    crc ^= data_in[i];
    crc ^= (unsigned char)(crc & 0xff) >> 4;
    crc ^= (crc << 8) << 4;
    crc ^= ((crc & 0xff) << 4) << 1;
  }

  return crc;
}

nRF8001::nRF8001(DigitalInOut *req, DigitalInOut *rdy, DigitalInOut *rst) :
  _pipeInfo(NULL),
  _numPipeInfo(0),

  _crcSeed(0xFFFF),

  _eventListener(NULL)
{
  this->_aciState.aci_pins.reqn_pin               = req;
  this->_aciState.aci_pins.rdyn_pin               = rdy;

  if (rst == NULL) {
    this->_aciState.aci_pins.board_name           = REDBEARLAB_SHIELD_V1_1;
  } 
  else {
    this->_aciState.aci_pins.board_name           = BOARD_DEFAULT;
  }

  this->_aciState.aci_pins.reset_pin              = rst;
  this->_aciState.aci_pins.active_pin             = NULL;

  this->_aciState.aci_pins.interface_is_interrupt = false;
  this->_aciState.aci_pins.interrupt_number       = 1;
}

nRF8001::~nRF8001() {
  if (this->_pipeInfo) {
    free(this->_pipeInfo);
  }
}



void nRF8001::setEventListener(nRF8001EventListener* eventListener) {
  this->_eventListener = eventListener;
}

void nRF8001::begin(const unsigned char* advertisementData,
                      unsigned char advertisementDataLength,
                      const unsigned char* scanData,
                      unsigned char scanDataLength,
                      BLEAttribute** attributes,
                      unsigned char numAttributes)
{
  unsigned char numPipedCharacteristics = 0;

  for (int i = 0; i < numAttributes; i++) {
    BLEAttribute* attribute = attributes[i];

    if (attribute->type() == BLETypeCharacteristic) {
      BLECharacteristic* characteristic = (BLECharacteristic *)attribute;

      if (characteristic->properties()) {
        numPipedCharacteristics++;
      }
    }
  }

  this->_pipeInfo = (struct pipeInfo*)malloc(sizeof(struct pipeInfo) * numPipedCharacteristics);

  lib_aci_init(&this->_aciState, false);

  this->waitForSetupMode();

  hal_aci_data_t setupMsg;
  struct setupMsgData* setupMsgData = (struct setupMsgData*)setupMsg.buffer;

  setupMsg.status_byte = 0;

  for (int i = 0; i < NB_BASE_SETUP_MESSAGES; i++) {
    int setupMsgSize = pgm_read_byte_near(&baseSetupMsgs[i].buffer[0]) + 2;

    memcpy_P(&setupMsg, &baseSetupMsgs[i], setupMsgSize);

    if (i == 1) {
      setupMsgData->data[6] = numPipedCharacteristics;
      setupMsgData->data[8] = numPipedCharacteristics;
    } else if (i == 2 && advertisementData && advertisementDataLength) {
      setupMsgData->data[22] = 0x40;
    } else if (i == 3 && scanData && scanDataLength) {
      setupMsgData->data[12] = 0x40;
    } else if (i == 5 && advertisementData && advertisementDataLength) {
      memcpy(setupMsgData->data, advertisementData, advertisementDataLength);
    } else if (i == 6 && scanData && scanDataLength) {
      memcpy(setupMsgData->data, scanData, scanDataLength);
    }

    this->sendSetupMessage(&setupMsg);
  }

  // GATT
  unsigned char  gattSetupMsgOffset = 0;
  unsigned short handle             = 1;
  unsigned char  pipe               = 1;
  unsigned char  numPiped           = 0;

  for (int i = 0; i < numAttributes; i++) {
    BLEAttribute* attribute = attributes[i];
    BLEUuid uuid = BLEUuid(attribute->uuid());

    if (attribute->type() == BLETypeService) {
      BLEService* service = (BLEService *)attribute;

      setupMsgData->length  = 12 + uuid.length();
      setupMsgData->cmd     = ACI_CMD_SETUP;
      setupMsgData->type    = 0x20;
      setupMsgData->offset  = gattSetupMsgOffset;

      setupMsgData->data[0] = 0x04;
      setupMsgData->data[1] = 0x04;
      setupMsgData->data[2] = uuid.length();
      setupMsgData->data[3] = uuid.length();

      setupMsgData->data[4] = (handle >> 8) & 0xff;
      setupMsgData->data[5] = handle & 0xff;
      handle++;

      setupMsgData->data[6] = (service->type() >> 8) & 0xff;
      setupMsgData->data[7] = service->type() & 0xff;

      setupMsgData->data[8] = ACI_STORE_LOCAL;

      memcpy(&setupMsgData->data[9], uuid.data(), uuid.length());

      gattSetupMsgOffset += 9 + uuid.length();

      this->sendSetupMessage(&setupMsg);
    } else if (attribute->type() == BLETypeCharacteristic) {
      BLECharacteristic* characteristic = (BLECharacteristic *)attribute;

      struct pipeInfo* pipeInfo = &this->_pipeInfo[numPiped];

      memset(pipeInfo, 0, sizeof(struct pipeInfo));

      pipeInfo->characteristic = characteristic;

      if (characteristic->properties()) {
        numPiped++;

        pipeInfo->startPipe = pipe;

        if (characteristic->properties() & BLENotify) {
          pipeInfo->txPipe = pipe;

          pipe++;
        }

        if (characteristic->properties() & BLEIndicate) {
          pipeInfo->txAckPipe = pipe;

          pipe++;
        }

        if (characteristic->properties() & BLEWriteWithoutResponse) {
          pipeInfo->rxPipe = pipe;

          pipe++;
        }

        if (characteristic->properties() & BLEWrite) {
          pipeInfo->rxAckPipe = pipe;

          pipe++;
        }

        if (characteristic->properties() & BLERead) {
          pipeInfo->setPipe = pipe;

          pipe++;
        }
      }

      setupMsgData->length   = 15 + uuid.length();
      setupMsgData->cmd      = ACI_CMD_SETUP;
      setupMsgData->type     = 0x20;
      setupMsgData->offset   = gattSetupMsgOffset;

      setupMsgData->data[0]  = 0x04;
      setupMsgData->data[1]  = 0x04;
      setupMsgData->data[2]  = 3 + uuid.length();
      setupMsgData->data[3]  = 3 + uuid.length();

      setupMsgData->data[4]  = (handle >> 8) & 0xff;
      setupMsgData->data[5]  = handle & 0xff;
      handle++;

      setupMsgData->data[6]  = (characteristic->type() >> 8) & 0xff;
      setupMsgData->data[7]  = characteristic->type() & 0xff;

      setupMsgData->data[8]  = ACI_STORE_LOCAL;
      setupMsgData->data[9]  = characteristic->properties();

      setupMsgData->data[10] = handle & 0xff;
      setupMsgData->data[11] = (handle >> 8) & 0xff;
      pipeInfo->valueHandle = handle;
      handle++;

      memcpy(&setupMsgData->data[12], uuid.data(), uuid.length());

      gattSetupMsgOffset += 12 + uuid.length();

      this->sendSetupMessage(&setupMsg);

      setupMsgData->length   = 12 + characteristic->valueSize();
      setupMsgData->cmd      = ACI_CMD_SETUP;
      setupMsgData->type     = 0x20;
      setupMsgData->offset   = gattSetupMsgOffset;

      setupMsgData->data[0]  = 0x04;
      setupMsgData->data[1]  = 0x00;

      if (characteristic->fixedLength()) {
        setupMsgData->data[0] |= 0x02;
      }

      if (characteristic->properties() & BLERead) {
        setupMsgData->data[1] |= 0x04;
      }

      if (characteristic->properties() & (BLEWrite | BLEWriteWithoutResponse)) {
        setupMsgData->data[0] |= 0x40;
        setupMsgData->data[1] |= 0x10;
      }

      if (characteristic->properties() & BLENotify) {
        setupMsgData->data[0] |= 0x10;
      }

      if (characteristic->properties() & BLEIndicate) {
        setupMsgData->data[0] |= 0x20;
      }

      setupMsgData->data[2]  = characteristic->valueSize();
      if (characteristic->fixedLength()) {
        setupMsgData->data[2]++;
      }

      setupMsgData->data[3]  = characteristic->valueLength();

      setupMsgData->data[4]  = (pipeInfo->valueHandle >> 8) & 0xff;
      setupMsgData->data[5]  = pipeInfo->valueHandle & 0xff;

      setupMsgData->data[6]  = 0x00;
      setupMsgData->data[7]  = 0x00;

      setupMsgData->data[8]  = 0x02;

      memset(&setupMsgData->data[9], 0x00, characteristic->valueSize());
      memcpy(&setupMsgData->data[9], characteristic->value(), characteristic->valueLength());

      gattSetupMsgOffset += 9 + characteristic->valueSize();

      this->sendSetupMessage(&setupMsg);

      if (characteristic->properties() & (BLENotify | BLEIndicate)) {
        setupMsgData->length   = 14;
        setupMsgData->cmd      = ACI_CMD_SETUP;
        setupMsgData->type     = 0x20;
        setupMsgData->offset   = gattSetupMsgOffset;

        setupMsgData->data[0]  = 0x46;
        setupMsgData->data[1]  = 0x14;

        setupMsgData->data[2]  = 0x03;
        setupMsgData->data[3]  = 0x02;

        setupMsgData->data[4]  = (handle >> 8) & 0xff;
        setupMsgData->data[5]  = handle & 0xff;
        pipeInfo->configHandle = handle;
        handle++;

        setupMsgData->data[6]  = 0x29;
        setupMsgData->data[7]  = 0x02;

        setupMsgData->data[8]  = ACI_STORE_LOCAL;

        setupMsgData->data[9]  = 0x00;
        setupMsgData->data[10] = 0x00;

        gattSetupMsgOffset += 11;

        this->sendSetupMessage(&setupMsg);
      }
    } else if (attribute->type() == BLETypeDescriptor) {
      BLEDescriptor* descriptor = (BLEDescriptor *)attribute;

      setupMsgData->length   = 12 + descriptor->valueSize();
      setupMsgData->cmd      = ACI_CMD_SETUP;
      setupMsgData->type     = 0x20;
      setupMsgData->offset   = gattSetupMsgOffset;

      setupMsgData->data[0]  = 0x04;
      setupMsgData->data[1]  = 0x04;

      setupMsgData->data[2]  = descriptor->valueSize();
      setupMsgData->data[3]  = descriptor->valueLength();

      setupMsgData->data[4]  = (handle >> 8) & 0xff;
      setupMsgData->data[5]  = handle & 0xff;
      handle++;

      setupMsgData->data[6]  = uuid.data()[1];
      setupMsgData->data[7]  = uuid.data()[0];

      setupMsgData->data[8]  = ACI_STORE_LOCAL;

      memcpy(&setupMsgData->data[9], descriptor->value(), descriptor->valueLength());

      gattSetupMsgOffset += 9 + descriptor->valueSize();

      this->sendSetupMessage(&setupMsg);
    }
  }

  this->_numPipeInfo = numPiped;

  // terminator
  setupMsgData->length   = 4;
  setupMsgData->cmd      = ACI_CMD_SETUP;
  setupMsgData->type     = 0x20;
  setupMsgData->offset   = gattSetupMsgOffset;

  setupMsgData->data[0]  = 0x00;

  gattSetupMsgOffset += 6;

  this->sendSetupMessage(&setupMsg);

  // pipes
  unsigned char pipeSetupMsgOffet  = 0;

  for (int i = 0; i < numPiped; i++) {
    struct pipeInfo pipeInfo = this->_pipeInfo[i];

    setupMsgData->length   = 13;
    setupMsgData->cmd      = ACI_CMD_SETUP;
    setupMsgData->type     = 0x40;
    setupMsgData->offset   = pipeSetupMsgOffet;

    setupMsgData->data[0]  = 0x00;
    setupMsgData->data[1]  = 0x00;

    setupMsgData->data[2]  = pipeInfo.startPipe;

    setupMsgData->data[3]  = 0x00;
    setupMsgData->data[4]  = 0x00;

    setupMsgData->data[5]  = 0x04;

    setupMsgData->data[6]  = (pipeInfo.valueHandle >> 8) & 0xff;
    setupMsgData->data[7]  = pipeInfo.valueHandle & 0xff;

    setupMsgData->data[8]  = (pipeInfo.configHandle >> 8) & 0xff;
    setupMsgData->data[9]  = pipeInfo.configHandle & 0xff;

    if (pipeInfo.characteristic->properties() & BLEIndicate) {
      setupMsgData->data[4] |= 0x04; // TX Ack
    }

    if (pipeInfo.characteristic->properties() & BLENotify) {
      setupMsgData->data[4] |= 0x02; // TX
    }

    if (pipeInfo.characteristic->properties() & BLEWriteWithoutResponse) {
      setupMsgData->data[4] |= 0x08; // RX Ack
    }

    if (pipeInfo.characteristic->properties() & BLEWrite) {
      setupMsgData->data[4] |= 0x10; // RX Ack
    }

    if (pipeInfo.characteristic->properties() & BLERead) {
      setupMsgData->data[4] |= 0x80; // Set
    }

    pipeSetupMsgOffet += 10;

    this->sendSetupMessage(&setupMsg);
  }

  this->sendCrc();
}

void nRF8001::poll() {
  // We enter the if statement only when there is a ACI event available to be processed
  if (lib_aci_event_get(&this->_aciState, &this->_aciData)) {
    aci_evt_t* aciEvt;
    aciEvt = &this->_aciData.evt;

    switch(aciEvt->evt_opcode) {
      /**
      As soon as you reset the nRF8001 you will get an ACI Device Started Event
      */
      case ACI_EVT_DEVICE_STARTED: {
        this->_aciState.data_credit_total = aciEvt->params.device_started.credit_available;
        switch(aciEvt->params.device_started.device_mode) {
          case ACI_DEVICE_SETUP:
            /**
            When the device is in the setup mode
            */
#ifdef NRF_8001_DEBUG
            Serial.println(F("Evt Device Started: Setup"));
#endif
            break;

          case ACI_DEVICE_STANDBY:
#ifdef NRF_8001_DEBUG
            Serial.println(F("Evt Device Started: Standby"));
#endif
            //Looking for an iPhone by sending radio advertisements
            //When an iPhone connects to us we will get an ACI_EVT_CONNECTED event from the nRF8001
            if (aciEvt->params.device_started.hw_error) {
              delay(20); //Handle the HW error event correctly.
            } else {
              lib_aci_connect(0/* in seconds : 0 means forever */, ADVERTISING_INTERVAL);
#ifdef NRF_8001_DEBUG
              Serial.println(F("Advertising started"));
#endif
            }
            break;
        }
      }
      break; //ACI Device Started Event

      case ACI_EVT_CMD_RSP:
        //If an ACI command response event comes with an error -> stop
        if (ACI_STATUS_SUCCESS != aciEvt->params.cmd_rsp.cmd_status) {
          //ACI ReadDynamicData and ACI WriteDynamicData will have status codes of
          //TRANSACTION_CONTINUE and TRANSACTION_COMPLETE
          //all other ACI commands will have status code of ACI_STATUS_SCUCCESS for a successful command
#ifdef NRF_8001_DEBUG
          Serial.print(F("ACI Command "));
          Serial.println(aciEvt->params.cmd_rsp.cmd_opcode, HEX);
          Serial.print(F("Evt Cmd respone: Status "));
          Serial.println(aciEvt->params.cmd_rsp.cmd_status, HEX);
#endif
        } else {
          switch (aciEvt->params.cmd_rsp.cmd_opcode) {
            case ACI_CMD_GET_DEVICE_VERSION:
              break;

            case ACI_CMD_GET_DEVICE_ADDRESS: {
#ifdef NRF_8001_DEBUG
              char address[18];

              sprintf(address, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
                aciEvt->params.cmd_rsp.params.get_device_address.bd_addr_own[5],
                aciEvt->params.cmd_rsp.params.get_device_address.bd_addr_own[4],
                aciEvt->params.cmd_rsp.params.get_device_address.bd_addr_own[3],
                aciEvt->params.cmd_rsp.params.get_device_address.bd_addr_own[2],
                aciEvt->params.cmd_rsp.params.get_device_address.bd_addr_own[1],
                aciEvt->params.cmd_rsp.params.get_device_address.bd_addr_own[0]);
              Serial.print(F("Device address = "));
              Serial.println(address);

              Serial.print(F("Device address type = "));
              Serial.println(aciEvt->params.cmd_rsp.params.get_device_address.bd_addr_type, DEC);
#endif
              if (this->_eventListener) {
                this->_eventListener->nRF8001AddressReceived(*this, aciEvt->params.cmd_rsp.params.get_device_address.bd_addr_own);
              }
              break;
            }

            case ACI_CMD_GET_BATTERY_LEVEL: {
              float batteryLevel = aciEvt->params.cmd_rsp.params.get_battery_level.battery_level * 0.00352;
#ifdef NRF_8001_DEBUG
              Serial.print(F("Battery level = "));
              Serial.println(batteryLevel);
#endif
              if (this->_eventListener) {
                this->_eventListener->nRF8001BatteryLevelReceived(*this, batteryLevel);
              }
              break;
            }

            case ACI_CMD_GET_TEMPERATURE: {
              float temperature = aciEvt->params.cmd_rsp.params.get_temperature.temperature_value / 4.0;
#ifdef NRF_8001_DEBUG
              Serial.print(F("Temperature = "));
              Serial.println(temperature);
#endif
              if (this->_eventListener) {
                this->_eventListener->nRF8001TemperatureReceived(*this, temperature);
              }
              break;
            }
          }
        }
        break;

      case ACI_EVT_CONNECTED:

#ifdef NRF_8001_DEBUG
        char address[18];
        sprintf(address, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
          aciEvt->params.connected.dev_addr[5],
          aciEvt->params.connected.dev_addr[4],
          aciEvt->params.connected.dev_addr[3],
          aciEvt->params.connected.dev_addr[2],
          aciEvt->params.connected.dev_addr[1],
          aciEvt->params.connected.dev_addr[0]);
        Serial.print(F("Evt Connected "));
        Serial.println(address);
#endif
        if (this->_eventListener) {
          this->_eventListener->nRF8001Connected(*this, aciEvt->params.connected.dev_addr);
        }

        this->_aciState.data_credit_available = this->_aciState.data_credit_total;
        break;

      case ACI_EVT_PIPE_STATUS:
#ifdef NRF_8001_DEBUG
        Serial.println(F("Evt Pipe Status "));

        uint64_t openPipes;
        uint64_t closedPipes;

        memcpy(&openPipes, aciEvt->params.pipe_status.pipes_open_bitmap, sizeof(openPipes));
        memcpy(&closedPipes, aciEvt->params.pipe_status.pipes_closed_bitmap, sizeof(closedPipes));

        Serial.println((unsigned long)openPipes, HEX);
        Serial.println((unsigned long)closedPipes, HEX);
#endif

        for (int i = 0; i < this->_numPipeInfo; i++) {
          struct pipeInfo* pipeInfo = &this->_pipeInfo[i];

          if (pipeInfo->txPipe) {
            pipeInfo->txPipeOpen = lib_aci_is_pipe_available(&this->_aciState, pipeInfo->txPipe);
          }

          if (pipeInfo->txAckPipe) {
            pipeInfo->txAckPipeOpen = lib_aci_is_pipe_available(&this->_aciState, pipeInfo->txAckPipe);
          }

          bool subscribed = (pipeInfo->txPipeOpen || pipeInfo->txAckPipeOpen);

          if (pipeInfo->characteristic->subscribed() != subscribed) {
            if (this->_eventListener) {
              this->_eventListener->nRF8001CharacteristicSubscribedChanged(*this, *pipeInfo->characteristic, subscribed);
            }
          }
        }
        break;

      case ACI_EVT_TIMING:
        break;

      case ACI_EVT_DISCONNECTED:
#ifdef NRF_8001_DEBUG
        Serial.println(F("Evt Disconnected/Advertising timed out"));
#endif
        // all characteristics unsubscribed on disconnect
        for (int i = 0; i < this->_numPipeInfo; i++) {
          struct pipeInfo* pipeInfo = &this->_pipeInfo[i];

          if (pipeInfo->characteristic->subscribed()) {
            if (this->_eventListener) {
              this->_eventListener->nRF8001CharacteristicSubscribedChanged(*this, *pipeInfo->characteristic, false);
            }
          }
        }

        if (this->_eventListener) {
          this->_eventListener->nRF8001Disconnected(*this);
        }

        lib_aci_connect(0/* in seconds  : 0 means forever */, ADVERTISING_INTERVAL);
#ifdef NRF_8001_DEBUG
        Serial.println(F("Advertising started."));
#endif
        break;

      case ACI_EVT_DATA_RECEIVED: {
        uint8_t dataLen = aciEvt->len - 2;
        uint8_t pipe = aciEvt->params.data_received.rx_data.pipe_number;
#ifdef NRF_8001_DEBUG
        Serial.print(F("Data Received, pipe = "));
        Serial.println(aciEvt->params.data_received.rx_data.pipe_number, DEC);

        for (int i = 0; i < dataLen; i++) {
          if ((aciEvt->params.data_received.rx_data.aci_data[i] & 0xf0) == 00) {
            Serial.print("0");
          }

          Serial.print(aciEvt->params.data_received.rx_data.aci_data[i], HEX);
          Serial.print(F(" "));
        }
        Serial.println();
#endif

        for (int i = 0; i < this->_numPipeInfo; i++) {
          struct pipeInfo* pipeInfo = &this->_pipeInfo[i];

          if (pipeInfo->rxAckPipe == pipe || pipeInfo->rxPipe == pipe) {
            if (pipeInfo->rxAckPipe == pipe) {
              lib_aci_send_ack(&this->_aciState, pipeInfo->rxAckPipe);
            }

            if (this->_eventListener) {
              this->_eventListener->nRF8001CharacteristicValueChanged(*this, *pipeInfo->characteristic, aciEvt->params.data_received.rx_data.aci_data, dataLen);
            }
            break;
          }
        }
      }
        break;

      case ACI_EVT_DATA_CREDIT:
        this->_aciState.data_credit_available = this->_aciState.data_credit_available + aciEvt->params.data_credit.credit;
        break;

      case ACI_EVT_PIPE_ERROR:
        //See the appendix in the nRF8001 Product Specication for details on the error codes
#ifdef NRF_8001_DEBUG
        Serial.print(F("ACI Evt Pipe Error: Pipe #:"));
        Serial.print(aciEvt->params.pipe_error.pipe_number, DEC);
        Serial.print(F("  Pipe Error Code: 0x"));
        Serial.println(aciEvt->params.pipe_error.error_code, HEX);
#endif

        //Increment the credit available as the data packet was not sent.
        //The pipe error also represents the Attribute protocol Error Response sent from the peer and that should not be counted
        //for the credit.
        if (ACI_STATUS_ERROR_PEER_ATT_ERROR != aciEvt->params.pipe_error.error_code) {
          this->_aciState.data_credit_available++;
        }
        break;

      case ACI_EVT_HW_ERROR:
#ifdef NRF_8001_DEBUG
        Serial.print(F("HW error: "));
        Serial.println(aciEvt->params.hw_error.line_num, DEC);

        for(uint8_t counter = 0; counter <= (aciEvt->len - 3); counter++) {
          Serial.write(aciEvt->params.hw_error.file_name[counter]); //uint8_t file_name[20];
        }
        Serial.println();
#endif
        lib_aci_connect(0/* in seconds, 0 means forever */, ADVERTISING_INTERVAL);
#ifdef NRF_8001_DEBUG
        Serial.println(F("Advertising started."));
#endif
        break;
    }
  } else {
    //Serial.println(F("No ACI Events available"));
    // No event in the ACI Event queue and if there is no event in the ACI command queue the arduino can go to sleep
    // Arduino can go to sleep now
    // Wakeup from sleep from the RDYN line
  }
}

bool nRF8001::updateCharacteristicValue(BLECharacteristic& characteristic) {
  bool success = true;

  for (int i = 0; i < this->_numPipeInfo; i++) {
    struct pipeInfo* pipeInfo = &this->_pipeInfo[i];

    if (pipeInfo->characteristic == &characteristic) {
      if (pipeInfo->setPipe) {
        success &= lib_aci_set_local_data(&this->_aciState, pipeInfo->setPipe, (uint8_t*)characteristic.value(), characteristic.valueLength());
      }

      if (pipeInfo->txPipe && pipeInfo->txPipeOpen) {
        if (this->canNotifyCharacteristic(characteristic)) {
          this->_aciState.data_credit_available--;
          success &= lib_aci_send_data(pipeInfo->txPipe, (uint8_t*)characteristic.value(), characteristic.valueLength());
        } else {
          success = false;
        }
      }

      if (pipeInfo->txAckPipe && pipeInfo->txAckPipeOpen) {
        if (this->canIndicateCharacteristic(characteristic)) {
          this->_aciState.data_credit_available--;
          success &= lib_aci_send_data(pipeInfo->txAckPipe, (uint8_t*)characteristic.value(), characteristic.valueLength());
        } else {
          success = false;
        }
      }

      break;
    }
  }

  return success;
}

bool nRF8001::canNotifyCharacteristic(BLECharacteristic& characteristic) {
  return (lib_aci_get_nb_available_credits(&this->_aciState) > 0);
}

bool nRF8001::canIndicateCharacteristic(BLECharacteristic& characteristic) {
  return (lib_aci_get_nb_available_credits(&this->_aciState) > 0);
}

void nRF8001::disconnect() {
  lib_aci_disconnect(&this->_aciState, ACI_REASON_TERMINATE);
}
void nRF8001::requestAddress() {
  lib_aci_get_address();
}

void nRF8001::requestTemperature() {
  lib_aci_get_temperature();
}

void nRF8001::requestBatteryLevel() {
  lib_aci_get_battery_level();
}

void nRF8001::waitForSetupMode()
{
  bool setupMode = false;

  while (!setupMode) {
    if (lib_aci_event_get(&this->_aciState, &this->_aciData)) {
      aci_evt_t* aciEvt = &this->_aciData.evt;

      switch(aciEvt->evt_opcode) {
        case ACI_EVT_DEVICE_STARTED: {
          switch(aciEvt->params.device_started.device_mode) {
            case ACI_DEVICE_SETUP:
              /**
              When the device is in the setup mode
              */
#ifdef NRF_8001_DEBUG
              Serial.println(F("Evt Device Started: Setup"));
#endif
              setupMode = true;
              break;
          }
        }
      }
    } else {
      delay(1);
    }
  }
}

void nRF8001::sendSetupMessage(hal_aci_data_t* data)
{
  this->_crcSeed = crc_16_ccitt(this->_crcSeed, data->buffer, data->buffer[0] + 1);

#ifdef NRF_8001_DEBUG
  for (int j = 0; j < (data->buffer[0] + 1); j++) {
    if ((data->buffer[j] & 0xf0) == 00) {
      Serial.print("0");
    }

    Serial.print(data->buffer[j], HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif

  hal_aci_tl_send(data);

  bool setupMsgSent = false;

  while (!setupMsgSent) {
    if (lib_aci_event_get(&this->_aciState, &this->_aciData)) {
      aci_evt_t* aciEvt = &this->_aciData.evt;

      switch(aciEvt->evt_opcode) {
        case ACI_EVT_CMD_RSP: {
          switch(aciEvt->params.cmd_rsp.cmd_status) {
            case ACI_STATUS_TRANSACTION_CONTINUE:
#ifdef NRF_8001_DEBUG
              Serial.println(F("Evt Cmd Rsp: Transaction Continue"));
#endif
              setupMsgSent = true;
              break;
          }
        }
      }
    } else {
      delay(1);
    }
  }
}

void nRF8001::sendCrc()
{
  hal_aci_data_t data;
  data.status_byte = 0;

  data.buffer[0]  = 3 + 3;
  data.buffer[1]  = ACI_CMD_SETUP;
  data.buffer[2]  = 0xf0;
  data.buffer[3]  = 0x00;

  data.buffer[4] = 0x03;

  this->_crcSeed = crc_16_ccitt(this->_crcSeed, data.buffer, data.buffer[0] - 1);

  data.buffer[5] = (this->_crcSeed >> 8) & 0xff;
  data.buffer[6] = this->_crcSeed & 0xff;

#ifdef NRF_8001_DEBUG
  for (int j = 0; j < (data.buffer[0] + 1); j++) {
    if ((data.buffer[j] & 0xf0) == 00) {
      Serial.print("0");
    }

    Serial.print(data.buffer[j], HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif

  hal_aci_tl_send(&data);

  bool setupMsgSent = false;

  while (!setupMsgSent) {
    if (lib_aci_event_get(&this->_aciState, &this->_aciData)) {
      aci_evt_t* aciEvt = &this->_aciData.evt;

      switch(aciEvt->evt_opcode) {
        case ACI_EVT_CMD_RSP: {
          switch(aciEvt->params.cmd_rsp.cmd_status) {
            case ACI_STATUS_TRANSACTION_COMPLETE:
#ifdef NRF_8001_DEBUG
              Serial.println(F("Evt Cmd Rsp: Transaction Complete"));
#endif
              setupMsgSent = true;
              break;
          }
        }
      }
    } else {
      delay(1);
    }
  }
}