|
- /*********************************************************************
- This is a library for our nRF8001 Bluetooth Low Energy Breakout
-
- Pick one up today in the adafruit shop!
- ------> http://www.adafruit.com/products/1697
-
- These displays use SPI to communicate, 4 or 5 pins are required to
- interface
-
- Adafruit invests time and resources providing this open source code,
- please support Adafruit and open-source hardware by purchasing
- products from Adafruit!
-
- Written by Kevin Townsend/KTOWN for Adafruit Industries.
- MIT license, check LICENSE for more information
- All text above, and the splash screen below must be included in any redistribution
- *********************************************************************/
- #include <SPI.h>
- #include <avr/pgmspace.h>
- #include <util/delay.h>
- #include <stdlib.h>
- #include <ble_system.h>
- #include <lib_aci.h>
- #include <aci_setup.h>
- #include "uart/services.h"
-
- #include "Adafruit_BLE_UART.h"
-
- /* Get the service pipe data created in nRFGo Studio */
- #ifdef SERVICES_PIPE_TYPE_MAPPING_CONTENT
- static services_pipe_type_mapping_t
- services_pipe_type_mapping[NUMBER_OF_PIPES] = SERVICES_PIPE_TYPE_MAPPING_CONTENT;
- #else
- #define NUMBER_OF_PIPES 0
- static services_pipe_type_mapping_t * services_pipe_type_mapping = NULL;
- #endif
-
- /* Length of the buffer used to store flash strings temporarily when printing. */
- #define PRINT_BUFFER_SIZE 20
-
- /* Store the setup for the nRF8001 in the flash of the AVR to save on RAM */
- static const hal_aci_data_t setup_msgs[NB_SETUP_MESSAGES] PROGMEM = SETUP_MESSAGES_CONTENT;
-
- static struct aci_state_t aci_state; /* ACI state data */
- static hal_aci_evt_t aci_data; /* Command buffer */
- static bool timing_change_done = false;
-
- // This is the Uart RX buffer, which we manage internally when data is available!
- #define ADAFRUIT_BLE_UART_RXBUFFER_SIZE 64
- uint8_t adafruit_ble_rx_buffer[ADAFRUIT_BLE_UART_RXBUFFER_SIZE];
- volatile uint16_t adafruit_ble_rx_head;
- volatile uint16_t adafruit_ble_rx_tail;
-
-
- int8_t HAL_IO_RADIO_RESET, HAL_IO_RADIO_REQN, HAL_IO_RADIO_RDY, HAL_IO_RADIO_IRQ;
-
- /**************************************************************************/
- /*!
- Constructor for the UART service
- */
- /**************************************************************************/
- // default RX callback!
-
- void Adafruit_BLE_UART::defaultRX(uint8_t *buffer, uint8_t len)
- {
- for(int i=0; i<len; i++)
- {
- uint16_t new_head = (uint16_t)(adafruit_ble_rx_head + 1) % ADAFRUIT_BLE_UART_RXBUFFER_SIZE;
-
- // if we should be storing the received character into the location
- // just before the tail (meaning that the head would advance to the
- // current location of the tail), we're about to overflow the buffer
- // and so we don't write the character or advance the head.
- if (new_head != adafruit_ble_rx_tail) {
- adafruit_ble_rx_buffer[adafruit_ble_rx_head] = buffer[i];
-
- // debug echo print
- // Serial.print((char)buffer[i]);
-
- adafruit_ble_rx_head = new_head;
- }
- }
-
- /*
- Serial.print("Buffer: ");
- for(int i=0; i<adafruit_ble_rx_head; i++)
- {
- Serial.print(" 0x"); Serial.print((char)adafruit_ble_rx_buffer[i], HEX);
- }
- Serial.println();
- */
- }
-
-
- /* Stream stuff */
-
- int Adafruit_BLE_UART::available(void)
- {
- return (uint16_t)(ADAFRUIT_BLE_UART_RXBUFFER_SIZE + adafruit_ble_rx_head - adafruit_ble_rx_tail)
- % ADAFRUIT_BLE_UART_RXBUFFER_SIZE;
- }
-
- int Adafruit_BLE_UART::read(void)
- {
- // if the head isn't ahead of the tail, we don't have any characters
- if (adafruit_ble_rx_head == adafruit_ble_rx_tail) {
- return -1;
- } else {
- unsigned char c = adafruit_ble_rx_buffer[adafruit_ble_rx_tail];
- adafruit_ble_rx_tail ++;
- adafruit_ble_rx_tail %= ADAFRUIT_BLE_UART_RXBUFFER_SIZE;
- return c;
- }
- }
-
- int Adafruit_BLE_UART::peek(void)
- {
- if (adafruit_ble_rx_head == adafruit_ble_rx_tail) {
- return -1;
- } else {
- return adafruit_ble_rx_buffer[adafruit_ble_rx_tail];
- }
- }
-
- void Adafruit_BLE_UART::flush(void)
- {
- // MEME: KTOWN what do we do here?
- }
-
-
-
- //// more callbacks
-
- void Adafruit_BLE_UART::defaultACICallback(aci_evt_opcode_t event)
- {
- currentStatus = event;
- }
-
- aci_evt_opcode_t Adafruit_BLE_UART::getState(void) {
- return currentStatus;
- }
-
-
-
- /**************************************************************************/
- /*!
- Constructor for the UART service
- */
- /**************************************************************************/
- Adafruit_BLE_UART::Adafruit_BLE_UART(int8_t req, int8_t rdy, int8_t rst)
- {
- debugMode = true;
-
- HAL_IO_RADIO_REQN = req;
- HAL_IO_RADIO_RDY = rdy;
- HAL_IO_RADIO_RESET = rst;
-
- rx_event = NULL;
- aci_event = NULL;
-
- memset(device_name, 0x00, 8);
-
- adafruit_ble_rx_head = adafruit_ble_rx_tail = 0;
-
- currentStatus = ACI_EVT_DISCONNECTED;
- }
-
- void Adafruit_BLE_UART::setACIcallback(aci_callback aciEvent) {
- aci_event = aciEvent;
- }
-
- void Adafruit_BLE_UART::setRXcallback(rx_callback rxEvent) {
- rx_event = rxEvent;
- }
-
- /**************************************************************************/
- /*!
- Transmits data out via the TX characteristic (when available)
- */
- /**************************************************************************/
- size_t Adafruit_BLE_UART::println(const char * thestr)
- {
- uint8_t len = strlen(thestr),
- written = len ? write((uint8_t *)thestr, len) : 0;
- if(written == len) written += write((uint8_t *)"\r\n", 2);
-
- return written;
- }
-
- size_t Adafruit_BLE_UART::print(const char * thestr)
- {
- return write((uint8_t *)thestr, strlen(thestr));
- }
-
- size_t Adafruit_BLE_UART::print(String thestr)
- {
- return write((uint8_t *)thestr.c_str(), thestr.length());
- }
-
- size_t Adafruit_BLE_UART::print(int theint)
- {
- char message[4*sizeof(int)+1] = {0};
- itoa(theint, message, 10);
- return write((uint8_t *)message, strlen(message));
- }
-
- size_t Adafruit_BLE_UART::print(const __FlashStringHelper *ifsh)
- {
- // Copy bytes from flash string into RAM, then send them a buffer at a time.
- char buffer[PRINT_BUFFER_SIZE] = {0};
- const char PROGMEM *p = (const char PROGMEM *)ifsh;
- size_t written = 0;
- int i = 0;
- unsigned char c = pgm_read_byte(p++);
- // Read data from flash until a null terminator is found.
- while (c != 0) {
- // Copy data to RAM and increase buffer index.
- buffer[i] = c;
- i++;
- if (i >= PRINT_BUFFER_SIZE) {
- // Send buffer when it's full and reset buffer index.
- written += write((uint8_t *)buffer, PRINT_BUFFER_SIZE);
- i = 0;
- }
- // Grab a new byte from flash.
- c = pgm_read_byte(p++);
- }
- if (i > 0) {
- // Send any remaining data in the buffer.
- written += write((uint8_t *)buffer, i);
- }
- return written;
- }
-
- size_t Adafruit_BLE_UART::write(uint8_t * buffer, uint8_t len)
- {
- uint8_t bytesThisPass, sent = 0;
-
- #ifdef BLE_RW_DEBUG
- Serial.print(F("\tWriting out to BTLE:"));
- for (uint8_t i=0; i<len; i++) {
- Serial.print(F(" 0x")); Serial.print(buffer[i], HEX);
- }
- Serial.println();
- #endif
-
- while(len) { // Parcelize into chunks
- bytesThisPass = len;
- if(bytesThisPass > ACI_PIPE_TX_DATA_MAX_LEN)
- bytesThisPass = ACI_PIPE_TX_DATA_MAX_LEN;
-
- if(!lib_aci_is_pipe_available(&aci_state, PIPE_UART_OVER_BTLE_UART_TX_TX))
- {
- pollACI();
- continue;
- }
-
- lib_aci_send_data(PIPE_UART_OVER_BTLE_UART_TX_TX, &buffer[sent],
- bytesThisPass);
- aci_state.data_credit_available--;
-
- delay(35); // required delay between sends
-
- if(!(len -= bytesThisPass)) break;
- sent += bytesThisPass;
- }
-
- return sent;
- }
-
- size_t Adafruit_BLE_UART::write(uint8_t buffer)
- {
- #ifdef BLE_RW_DEBUG
- Serial.print(F("\tWriting one byte 0x")); Serial.println(buffer, HEX);
- #endif
- if (lib_aci_is_pipe_available(&aci_state, PIPE_UART_OVER_BTLE_UART_TX_TX))
- {
- lib_aci_send_data(PIPE_UART_OVER_BTLE_UART_TX_TX, &buffer, 1);
- aci_state.data_credit_available--;
-
- delay(35); // required delay between sends
- return 1;
- }
-
- pollACI();
-
- return 0;
- }
-
- /**************************************************************************/
- /*!
- Update the device name (7 characters or less!)
- */
- /**************************************************************************/
- void Adafruit_BLE_UART::setDeviceName(const char * deviceName)
- {
- if (strlen(deviceName) > 7)
- {
- /* String too long! */
- return;
- }
- else
- {
- memcpy(device_name, deviceName, strlen(deviceName));
- }
- }
-
- /**************************************************************************/
- /*!
- Handles low level ACI events, and passes them up to an application
- level callback when appropriate
- */
- /**************************************************************************/
- void Adafruit_BLE_UART::pollACI()
- {
- // We enter the if statement only when there is a ACI event available to be processed
- if (lib_aci_event_get(&aci_state, &aci_data))
- {
- aci_evt_t * aci_evt;
-
- aci_evt = &aci_data.evt;
- switch(aci_evt->evt_opcode)
- {
- /* As soon as you reset the nRF8001 you will get an ACI Device Started Event */
- case ACI_EVT_DEVICE_STARTED:
- {
- aci_state.data_credit_total = aci_evt->params.device_started.credit_available;
- switch(aci_evt->params.device_started.device_mode)
- {
- case ACI_DEVICE_SETUP:
- /* Device is in setup mode! */
- if (ACI_STATUS_TRANSACTION_COMPLETE != do_aci_setup(&aci_state))
- {
- if (debugMode) {
- Serial.println(F("Error in ACI Setup"));
- }
- }
- break;
-
- case ACI_DEVICE_STANDBY:
- /* Start advertising ... first value is advertising time in seconds, the */
- /* second value is the advertising interval in 0.625ms units */
- if (device_name[0] != 0x00)
- {
- /* Update the device name */
- lib_aci_set_local_data(&aci_state, PIPE_GAP_DEVICE_NAME_SET , (uint8_t *)&device_name, strlen(device_name));
- }
- lib_aci_connect(adv_timeout, adv_interval);
- defaultACICallback(ACI_EVT_DEVICE_STARTED);
- if (aci_event)
- aci_event(ACI_EVT_DEVICE_STARTED);
- }
- }
- break;
-
- case ACI_EVT_CMD_RSP:
- /* If an ACI command response event comes with an error -> stop */
- if (ACI_STATUS_SUCCESS != aci_evt->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_SUCCESS for a successful command
- if (debugMode) {
- Serial.print(F("ACI Command "));
- Serial.println(aci_evt->params.cmd_rsp.cmd_opcode, HEX);
- Serial.println(F("Evt Cmd respone: Error. Arduino is in an while(1); loop"));
- }
- while (1);
- }
- if (ACI_CMD_GET_DEVICE_VERSION == aci_evt->params.cmd_rsp.cmd_opcode)
- {
- // Store the version and configuration information of the nRF8001 in the Hardware Revision String Characteristic
- lib_aci_set_local_data(&aci_state, PIPE_DEVICE_INFORMATION_HARDWARE_REVISION_STRING_SET,
- (uint8_t *)&(aci_evt->params.cmd_rsp.params.get_device_version), sizeof(aci_evt_cmd_rsp_params_get_device_version_t));
- }
- break;
-
- case ACI_EVT_CONNECTED:
- aci_state.data_credit_available = aci_state.data_credit_total;
- /* Get the device version of the nRF8001 and store it in the Hardware Revision String */
- lib_aci_device_version();
-
- defaultACICallback(ACI_EVT_CONNECTED);
- if (aci_event)
- aci_event(ACI_EVT_CONNECTED);
-
- case ACI_EVT_PIPE_STATUS:
- if (lib_aci_is_pipe_available(&aci_state, PIPE_UART_OVER_BTLE_UART_TX_TX) && (false == timing_change_done))
- {
- lib_aci_change_timing_GAP_PPCP(); // change the timing on the link as specified in the nRFgo studio -> nRF8001 conf. -> GAP.
- // Used to increase or decrease bandwidth
- timing_change_done = true;
- }
- break;
-
- case ACI_EVT_TIMING:
- /* Link connection interval changed */
- break;
-
- case ACI_EVT_DISCONNECTED:
- /* Restart advertising ... first value is advertising time in seconds, the */
- /* second value is the advertising interval in 0.625ms units */
-
- defaultACICallback(ACI_EVT_DISCONNECTED);
- if (aci_event)
- aci_event(ACI_EVT_DISCONNECTED);
-
- lib_aci_connect(adv_timeout, adv_interval);
-
- defaultACICallback(ACI_EVT_DEVICE_STARTED);
- if (aci_event)
- aci_event(ACI_EVT_DEVICE_STARTED);
- break;
-
- case ACI_EVT_DATA_RECEIVED:
- defaultRX(aci_evt->params.data_received.rx_data.aci_data, aci_evt->len - 2);
- if (rx_event)
- rx_event(aci_evt->params.data_received.rx_data.aci_data, aci_evt->len - 2);
- break;
-
- case ACI_EVT_DATA_CREDIT:
- aci_state.data_credit_available = aci_state.data_credit_available + aci_evt->params.data_credit.credit;
- break;
-
- case ACI_EVT_PIPE_ERROR:
- /* See the appendix in the nRF8001 Product Specication for details on the error codes */
- if (debugMode) {
- Serial.print(F("ACI Evt Pipe Error: Pipe #:"));
- Serial.print(aci_evt->params.pipe_error.pipe_number, DEC);
- Serial.print(F(" Pipe Error Code: 0x"));
- Serial.println(aci_evt->params.pipe_error.error_code, HEX);
- }
-
- /* Increment the credit available as the data packet was not sent */
- aci_state.data_credit_available++;
- 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
- }
- }
-
- /**************************************************************************/
- /*!
- Configures the nRF8001 and starts advertising the UART Service
-
- @param[in] advTimeout
- The advertising timeout in seconds (0 = infinite advertising)
- @param[in] advInterval
- The delay between advertising packets in 0.625ms units
- */
- /**************************************************************************/
- bool Adafruit_BLE_UART::begin(uint16_t advTimeout, uint16_t advInterval)
- {
- /* Store the advertising timeout and interval */
- adv_timeout = advTimeout; /* ToDo: Check range! */
- adv_interval = advInterval; /* ToDo: Check range! */
-
- /* Setup the service data from nRFGo Studio (services.h) */
- if (NULL != services_pipe_type_mapping)
- {
- aci_state.aci_setup_info.services_pipe_type_mapping = &services_pipe_type_mapping[0];
- }
- else
- {
- aci_state.aci_setup_info.services_pipe_type_mapping = NULL;
- }
- aci_state.aci_setup_info.number_of_pipes = NUMBER_OF_PIPES;
- aci_state.aci_setup_info.setup_msgs = (hal_aci_data_t*)setup_msgs;
- aci_state.aci_setup_info.num_setup_msgs = NB_SETUP_MESSAGES;
-
- /* Pass the service data into the appropriate struct in the ACI */
- lib_aci_init(&aci_state);
-
- /* ToDo: Check for chip ID to make sure we're connected! */
-
- return true;
- }
|