diff options
Diffstat (limited to 'arduino/libraries/Bluefruit52Lib/src/services/BLEUart.cpp')
-rwxr-xr-x | arduino/libraries/Bluefruit52Lib/src/services/BLEUart.cpp | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.cpp new file mode 100755 index 0000000..050ecc8 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.cpp @@ -0,0 +1,325 @@ +/**************************************************************************/ +/*! + @file BLEUart.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" +#include "utility/TimeoutTimer.h" + +/* UART Serivce: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E + * UART RXD : 6E400002-B5A3-F393-E0A9-E50E24DCCA9E + * UART TXD : 6E400003-B5A3-F393-E0A9-E50E24DCCA9E + */ + +const uint8_t BLEUART_UUID_SERVICE[] = +{ + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x01, 0x00, 0x40, 0x6E +}; + +const uint8_t BLEUART_UUID_CHR_RXD[] = +{ + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x02, 0x00, 0x40, 0x6E +}; + +const uint8_t BLEUART_UUID_CHR_TXD[] = +{ + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x03, 0x00, 0x40, 0x6E +}; + +/** + * Constructor + */ +BLEUart::BLEUart(uint16_t fifo_depth) + : BLEService(BLEUART_UUID_SERVICE), _txd(BLEUART_UUID_CHR_TXD), _rxd(BLEUART_UUID_CHR_RXD) +{ + _rx_fifo = NULL; + _rx_cb = NULL; + _rx_fifo_depth = fifo_depth; + + _tx_fifo = NULL; + _tx_buffered = 0; + _buffered_th = NULL; +} + +/** + * Destructor + */ +BLEUart::~BLEUart() +{ + if ( _tx_fifo ) delete _tx_fifo; +} + +/** + * Callback when received new data + * @param chr + * @param data + * @param len + * @param offset + */ +void bleuart_rxd_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset) +{ + (void) offset; + + BLEUart& svc = (BLEUart&) chr.parentService(); + svc._rx_fifo->write(data, len); + +#if CFG_DEBUG >= 2 + LOG_LV2("BLEUART", "RX: "); + PRINT_BUFFER(data, len); +#endif + + // invoke user callback + if ( svc._rx_cb ) svc._rx_cb(); +} + +/** + * Timer callback periodically to send TX packet (if enabled). + * @param timer + */ +void bleuart_txd_buffered_hdlr(TimerHandle_t timer) +{ + BLEUart* svc = (BLEUart*) pvTimerGetTimerID(timer); + + // skip if null (unlikely) + if ( !svc->_tx_fifo ) return; + + // flush tx data + (void) svc->flush_tx_buffered(); +} + +void bleuart_txd_cccd_cb(BLECharacteristic& chr, uint16_t value) +{ + BLEUart& svc = (BLEUart&) chr.parentService(); + + if ( svc._buffered_th == NULL) return; + + // Enable TXD timer if configured + if (value & BLE_GATT_HVX_NOTIFICATION) + { + xTimerStart(svc._buffered_th, 0); // if started --> timer got reset + }else + { + xTimerStop(svc._buffered_th, 0); + } +} + +void BLEUart::setRxCallback( rx_callback_t fp) +{ + _rx_cb = fp; +} + +/** + * Enable packet buffered for TXD + * Note: packet is sent right away if it reach MTU bytes + * @param enable true or false + */ +void BLEUart::bufferTXD(uint8_t enable) +{ + _tx_buffered = enable; + + if ( enable ) + { + // enable cccd callback to start timer when enabled + _txd.setCccdWriteCallback(bleuart_txd_cccd_cb); + + // Create FIFO for TX TODO Larger MTU Size + if ( _tx_fifo == NULL ) + { + _tx_fifo = new Adafruit_FIFO(1); + _tx_fifo->begin( Bluefruit.Gap.getMaxMtuByConnCfg(CONN_CFG_PERIPHERAL) ); + } + }else + { + _txd.setCccdWriteCallback(NULL); + + if ( _tx_fifo ) delete _tx_fifo; + } +} + +err_t BLEUart::begin(void) +{ + _rx_fifo = new Adafruit_FIFO(1); + _rx_fifo->begin(_rx_fifo_depth); + + // Invoke base class begin() + VERIFY_STATUS( BLEService::begin() ); + + uint16_t max_mtu = Bluefruit.Gap.getMaxMtuByConnCfg(CONN_CFG_PERIPHERAL); + + // Add TXD Characteristic + _txd.setProperties(CHR_PROPS_NOTIFY); + // TODO enable encryption when bonding is enabled + _txd.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + _txd.setMaxLen( max_mtu ); + _txd.setUserDescriptor("TXD"); + VERIFY_STATUS( _txd.begin() ); + + // Add RXD Characteristic + _rxd.setProperties(CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP); + _rxd.setWriteCallback(bleuart_rxd_cb); + + // TODO enable encryption when bonding is enabled + _rxd.setPermission(SECMODE_NO_ACCESS, SECMODE_OPEN); + _rxd.setMaxLen( max_mtu ); + _rxd.setUserDescriptor("RXD"); + VERIFY_STATUS(_rxd.begin()); + + return ERROR_NONE; +} + +bool BLEUart::notifyEnabled(void) +{ + return _txd.notifyEnabled(); +} + +void BLEUart::_disconnect_cb(void) +{ + if (_buffered_th) + { + xTimerDelete(_buffered_th, 0); + _buffered_th = NULL; + + if (_tx_fifo) _tx_fifo->clear(); + } +} + +void BLEUart::_connect_cb (void) +{ + if ( _tx_buffered ) + { + // create TXD timer TODO take connInterval into account + // ((5*ms2tick(Bluefruit.connInterval())) / 4) / 2 + _buffered_th = xTimerCreate(NULL, ms2tick(10), true, this, bleuart_txd_buffered_hdlr); + + // Start the timer + xTimerStart(_buffered_th, 0); + } +} + +/*------------------------------------------------------------------*/ +/* STREAM API + *------------------------------------------------------------------*/ +int BLEUart::read (void) +{ + uint8_t ch; + return read(&ch, 1) ? (int) ch : EOF; +} + +int BLEUart::read (uint8_t * buf, size_t size) +{ + return _rx_fifo->read(buf, size); +} + +size_t BLEUart::write (uint8_t b) +{ + return write(&b, 1); +} + +size_t BLEUart::write (const uint8_t *content, size_t len) +{ + // notify right away if txd buffered is not enabled + if ( !(_tx_buffered && _tx_fifo) ) + { + return _txd.notify(content, len) ? len : 0; + }else + { + // skip if not enabled + if ( !notifyEnabled() ) return 0; + + uint16_t written = _tx_fifo->write(content, len); + + // TODO multiple prph connections + // Not up to GATT MTU, notify will be sent later by TXD timer handler + if ( _tx_fifo->count() < (Bluefruit.Gap.getMTU( Bluefruit.connHandle() ) - 3) ) + { + return len; + } + else + { + // TX fifo has enough data, send notify right away + VERIFY( flush_tx_buffered(), 0); + + // still more data left, send them all + if ( written < len ) + { + VERIFY( _txd.notify(content+written, len-written), written); + } + + return len; + } + } +} + +int BLEUart::available (void) +{ + return _rx_fifo->count(); +} + +int BLEUart::peek (void) +{ + uint8_t ch; + return _rx_fifo->peek(&ch) ? (int) ch : EOF; +} + +void BLEUart::flush (void) +{ + _rx_fifo->clear(); +} + +bool BLEUart::flush_tx_buffered(void) +{ + uint16_t max_hvx = Bluefruit.Gap.getMTU( Bluefruit.connHandle() ) - 3; + uint8_t* ff_data = (uint8_t*) rtos_malloc( max_hvx ); + + if (!ff_data) return false; + + uint16_t len = _tx_fifo->read(ff_data, max_hvx); + bool result = true; + + if ( len ) + { + result = _txd.notify(ff_data, len); + } + + rtos_free(ff_data); + + return result; +} + + + + |