Attempting to publish a tree

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MicroBitUARTService.cpp Source File

MicroBitUARTService.cpp

00001 /*
00002 The MIT License (MIT)
00003 
00004 Copyright (c) 2016 British Broadcasting Corporation.
00005 This software is provided by Lancaster University by arrangement with the BBC.
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a
00008 copy of this software and associated documentation files (the "Software"),
00009 to deal in the Software without restriction, including without limitation
00010 the rights to use, copy, modify, merge, publish, distribute, sublicense,
00011 and/or sell copies of the Software, and to permit persons to whom the
00012 Software is furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00023 DEALINGS IN THE SOFTWARE.
00024 */
00025 
00026 /**
00027   * Class definition for the custom MicroBit UART Service.
00028   * Provides a BLE service that acts as a UART port, enabling the reception and transmission
00029   * of an arbitrary number of bytes.
00030   */
00031 
00032 #include "ble/UUID.h"
00033 
00034 #include "ExternalEvents.h"
00035 #include "MicroBitUARTService.h"
00036 #include "MicroBitFiber.h"
00037 #include "ErrorNo.h"
00038 #include "NotifyEvents.h"
00039 
00040 static uint8_t txBufferHead = 0;
00041 static uint8_t txBufferTail = 0;
00042 
00043 static GattCharacteristic* rxCharacteristic = NULL;
00044 
00045 /**
00046   * A callback function for whenever a Bluetooth device consumes our RX Buffer
00047   */
00048 void on_confirmation_received_callback(uint16_t handle)
00049 {
00050 #if CONFIG_ENABLED(MICROBIT_DBG)
00051     SERIAL_DEBUG->printf("RECEIVED!! %d \r\n",handle);
00052 #endif
00053     if(handle == rxCharacteristic->getValueAttribute().getHandle())
00054     {
00055         txBufferTail = txBufferHead;
00056         MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY);
00057     }
00058 }
00059 
00060 /**
00061  * Constructor for the UARTService.
00062  * @param _ble an instance of BLEDevice
00063  * @param rxBufferSize the size of the rxBuffer
00064  * @param txBufferSize the size of the txBuffer
00065  *
00066  * @note defaults to 20
00067  */
00068 MicroBitUARTService::MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize, uint8_t txBufferSize) : ble(_ble)
00069 {
00070 
00071     txBuffer = (uint8_t *)malloc(txBufferSize);
00072     rxBuffer = (uint8_t *)malloc(rxBufferSize);
00073 
00074     rxBufferHead = 0;
00075     rxBufferTail = 0;
00076     this->rxBufferSize = rxBufferSize;
00077 
00078     txBufferHead = 0;
00079     txBufferTail = 0;
00080     this->txBufferSize = txBufferSize;
00081 
00082     GattCharacteristic txCharacteristic(UARTServiceTXCharacteristicUUID, rxBuffer, 1, rxBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
00083 
00084     rxCharacteristic = new GattCharacteristic(UARTServiceRXCharacteristicUUID, txBuffer, 1, txBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);
00085 
00086     GattCharacteristic *charTable[] = {&txCharacteristic, rxCharacteristic};
00087 
00088     GattService uartService(UARTServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
00089 
00090     _ble.addService(uartService);
00091 
00092     this->txCharacteristicHandle = txCharacteristic.getValueAttribute().getHandle();
00093 
00094     _ble.gattServer().onDataWritten(this, &MicroBitUARTService::onDataWritten);
00095     _ble.gattServer().onConfirmationReceived(on_confirmation_received_callback);
00096 }
00097 
00098 /**
00099   * A callback function for whenever a Bluetooth device writes to our TX characteristic.
00100   */
00101 void MicroBitUARTService::onDataWritten(const GattWriteCallbackParams *params) {
00102     if (params->handle == this->txCharacteristicHandle)
00103     {
00104         uint16_t bytesWritten = params->len;
00105 
00106         for(int byteIterator = 0; byteIterator <  bytesWritten; byteIterator++)
00107         {
00108             int newHead = (rxBufferHead + 1) % rxBufferSize;
00109 
00110             if(newHead != rxBufferTail)
00111             {
00112                 char c = params->data[byteIterator];
00113 
00114                 int delimeterOffset = 0;
00115                 int delimLength = this->delimeters.length();
00116 
00117                 //iterate through our delimeters (if any) to see if there is a match
00118                 while(delimeterOffset < delimLength)
00119                 {
00120                     //fire an event if there is to block any waiting fibers
00121                     if(this->delimeters.charAt(delimeterOffset) == c)
00122                         MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH);
00123 
00124                     delimeterOffset++;
00125                 }
00126 
00127                 rxBuffer[rxBufferHead] = c;
00128 
00129                 rxBufferHead = newHead;
00130 
00131                 if(rxBufferHead == rxBuffHeadMatch)
00132                 {
00133                     rxBuffHeadMatch = -1;
00134                     MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH);
00135                 }
00136             }
00137             else
00138                 MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_RX_FULL);
00139         }
00140     }
00141 }
00142 
00143 /**
00144   * An internal method that copies values from a circular buffer to a linear buffer.
00145   *
00146   * @param circularBuff a pointer to the source circular buffer
00147   * @param circularBuffSize the size of the circular buffer
00148   * @param linearBuff a pointer to the destination linear buffer
00149   * @param tailPosition the tail position in the circular buffer you want to copy from
00150   * @param headPosition the head position in the circular buffer you want to copy to
00151   *
00152   * @note this method assumes that the linear buffer has the appropriate amount of
00153   *       memory to contain the copy operation
00154   */
00155 void MicroBitUARTService::circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition)
00156 {
00157     int toBuffIndex = 0;
00158 
00159     while(tailPosition != headPosition)
00160     {
00161         linearBuff[toBuffIndex++] = circularBuff[tailPosition];
00162 
00163         tailPosition = (tailPosition + 1) % circularBuffSize;
00164     }
00165 }
00166 
00167 /**
00168   * Retreives a single character from our RxBuffer.
00169   *
00170   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00171   *        gives a different behaviour:
00172   *
00173   *            ASYNC - Will attempt to read a single character, and return immediately
00174   *
00175   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00176   *
00177   *            SYNC_SLEEP - Will configure the event and block the current fiber until the
00178   *                         event is received.
00179   *
00180   * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, a character or MICROBIT_NO_DATA
00181   */
00182 int MicroBitUARTService::getc(MicroBitSerialMode mode)
00183 {
00184     if(mode == SYNC_SPINWAIT)
00185         return MICROBIT_INVALID_PARAMETER;
00186 
00187     if(mode == ASYNC)
00188     {
00189         if(!isReadable())
00190             return MICROBIT_NO_DATA;
00191     }
00192 
00193     if(mode == SYNC_SLEEP)
00194     {
00195         if(!isReadable())
00196             eventAfter(1, mode);
00197     }
00198 
00199     char c = rxBuffer[rxBufferTail];
00200 
00201     rxBufferTail = (rxBufferTail + 1) % rxBufferSize;
00202 
00203     return c;
00204 }
00205 
00206 /**
00207   * places a single character into our transmission buffer,
00208   *
00209   * @param c the character to transmit
00210   *
00211   * @return the number of characters written (0, or 1).
00212   */
00213 int MicroBitUARTService::putc(char c)
00214 {
00215     return (send((uint8_t *)&c, 1) == 1) ? 1 : EOF;
00216 }
00217 
00218 /**
00219   * Copies characters into the buffer used for Transmitting to the central device.
00220   *
00221   * @param buf a buffer containing length number of bytes.
00222   * @param length the size of the buffer.
00223   *
00224   * @return the number of characters copied into the buffer
00225   *
00226   * @note no modes for sending are available at the moment, due to interrupt overhead.
00227   */
00228 int MicroBitUARTService::send(const uint8_t *buf, int length)
00229 {
00230     if(length < 1)
00231         return MICROBIT_INVALID_PARAMETER;
00232 
00233     int bytesWritten = 0;
00234 
00235     if (ble.getGapState().connected) {
00236 
00237         for(int bufferIterator = 0; bufferIterator < length; bufferIterator++)
00238         {
00239             int nextHead = (txBufferHead + 1) % txBufferSize;
00240 
00241             if(nextHead != txBufferTail)
00242             {
00243                 txBuffer[txBufferHead] = buf[bufferIterator];
00244 
00245                 txBufferHead = nextHead;
00246 
00247                 bytesWritten++;
00248             }
00249         }
00250 
00251         int size = txBufferedSize ();
00252 
00253 #if CONFIG_ENABLED(MICROBIT_DBG)
00254         SERIAL_DEBUG->printf("tx size: %d", size);
00255 #endif
00256 
00257         uint8_t temp[size];
00258 
00259         memclr(&temp, size);
00260 
00261         circularCopy(txBuffer, txBufferSize, temp, txBufferTail, txBufferHead);
00262 
00263 #if CONFIG_ENABLED(MICROBIT_DBG)
00264         for(int i = 0; i < size; i++)
00265             SERIAL_DEBUG->printf("%c",temp[i]);
00266 #endif
00267 
00268         ble.gattServer().write(rxCharacteristic->getValueAttribute().getHandle(), temp, size);
00269     }
00270 
00271 #if CONFIG_ENABLED(MICROBIT_DBG)
00272     SERIAL_DEBUG->printf("written: %d \r\n",bytesWritten);
00273 #endif
00274 
00275     return bytesWritten;
00276 }
00277 
00278 /**
00279   * Copies characters into the buffer used for Transmitting to the central device.
00280   *
00281   * @param s the string to transmit
00282   *
00283   * @return the number of characters copied into the buffer
00284   *
00285   * @note no modes for sending are available at the moment, due to interrupt overhead.
00286   */
00287 int MicroBitUARTService::send(ManagedString s)
00288 {
00289     return send((uint8_t *)s.toCharArray(), s.length());
00290 }
00291 
00292 /**
00293   * Reads a number of characters from the rxBuffer and fills user given buffer.
00294   *
00295   * @param buf a pointer to a buffer of len bytes.
00296   * @param len the size of the user allocated buffer
00297   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00298   *        gives a different behaviour:
00299   *
00300   *            ASYNC - Will attempt to read all available characters, and return immediately
00301   *                    until the buffer limit is reached
00302   *
00303   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00304   *
00305   *            SYNC_SLEEP - Will first of all determine whether the given number of characters
00306   *                         are available in our buffer, if not, it will set an event and sleep
00307   *                         until the number of characters are avaialable.
00308   *
00309   * @return the number of characters digested
00310   */
00311 int MicroBitUARTService::read(uint8_t *buf, int len, MicroBitSerialMode mode)
00312 {
00313     if(mode == SYNC_SPINWAIT)
00314         return MICROBIT_INVALID_PARAMETER;
00315 
00316     int i = 0;
00317 
00318     if(mode == ASYNC)
00319     {
00320         int c;
00321 
00322         while((c = getc(mode)) > 0 && i < len)
00323         {
00324             buf[i] = c;
00325             i++;
00326         }
00327     }
00328 
00329     if(mode == SYNC_SLEEP)
00330     {
00331         if(len > rxBufferedSize ())
00332             eventAfter(len - rxBufferedSize (), mode);
00333 
00334         while(i < len)
00335         {
00336             buf[i] = (char)getc(mode);
00337             i++;
00338         }
00339     }
00340 
00341     return i;
00342 }
00343 
00344 /**
00345   * Reads a number of characters from the rxBuffer and returns them as a ManagedString
00346   *
00347   * @param len the number of characters to read.
00348   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00349   *        gives a different behaviour:
00350   *
00351   *            ASYNC - Will attempt to read all available characters, and return immediately
00352   *                    until the buffer limit is reached
00353   *
00354   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00355   *
00356   *            SYNC_SLEEP - Will first of all determine whether the given number of characters
00357   *                         are available in our buffer, if not, it will set an event and sleep
00358   *                         until the number of characters are avaialable.
00359   *
00360   * @return an empty ManagedString on error, or a ManagedString containing characters
00361   */
00362 ManagedString MicroBitUARTService::read(int len, MicroBitSerialMode mode)
00363 {
00364     uint8_t buf[len + 1];
00365 
00366     memclr(&buf, len + 1);
00367 
00368     int ret = read(buf, len, mode);
00369 
00370     if(ret < 1)
00371         return ManagedString();
00372 
00373     return ManagedString((const char *)buf);
00374 }
00375 
00376 /**
00377   * Reads characters until a character matches one of the given delimeters
00378   *
00379   * @param delimeters the number of characters to match against
00380   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00381   *        gives a different behaviour:
00382   *
00383   *            ASYNC - Will attempt read the immediate buffer, and look for a match.
00384   *                    If there isn't, an empty ManagedString will be returned.
00385   *
00386   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00387   *
00388   *            SYNC_SLEEP - Will first of all consider the characters in the immediate buffer,
00389   *                         if a match is not found, it will block on an event, fired when a
00390   *                         character is matched.
00391   *
00392   * @return an empty ManagedString on error, or a ManagedString containing characters
00393   */
00394 ManagedString MicroBitUARTService::readUntil(ManagedString delimeters, MicroBitSerialMode mode)
00395 {
00396     if(mode == SYNC_SPINWAIT)
00397         return MICROBIT_INVALID_PARAMETER;
00398 
00399     int localTail = rxBufferTail;
00400     int preservedTail = rxBufferTail;
00401 
00402     int foundIndex = -1;
00403 
00404     //ASYNC mode just iterates through our stored characters checking for any matches.
00405     while(localTail != rxBufferHead && foundIndex  == -1)
00406     {
00407         //we use localTail to prevent modification of the actual tail.
00408         char c = rxBuffer[localTail];
00409 
00410         for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++)
00411             if(delimeters.charAt(delimeterIterator) == c)
00412                 foundIndex = localTail;
00413 
00414         localTail = (localTail + 1) % rxBufferSize;
00415     }
00416 
00417     //if our mode is SYNC_SLEEP, we set up an event to be fired when we see a
00418     //matching character.
00419     if(mode == SYNC_SLEEP && foundIndex == -1)
00420     {
00421         eventOn(delimeters, mode);
00422 
00423         foundIndex = rxBufferHead - 1;
00424 
00425         this->delimeters = ManagedString();
00426     }
00427 
00428     if(foundIndex >= 0)
00429     {
00430         //calculate our local buffer size
00431         int localBuffSize = (preservedTail > foundIndex) ? (rxBufferSize - preservedTail) + foundIndex : foundIndex - preservedTail;
00432 
00433         uint8_t localBuff[localBuffSize + 1];
00434 
00435         memclr(&localBuff, localBuffSize + 1);
00436 
00437         circularCopy(rxBuffer, rxBufferSize, localBuff, preservedTail, foundIndex);
00438 
00439         //plus one for the character we listened for...
00440         rxBufferTail = (rxBufferTail + localBuffSize + 1) % rxBufferSize;
00441 
00442         return ManagedString((char *)localBuff, localBuffSize);
00443     }
00444 
00445     return ManagedString();
00446 }
00447 
00448 /**
00449   * Configures an event to be fired on a match with one of the delimeters.
00450   *
00451   * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n")
00452   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00453   *        gives a different behaviour:
00454   *
00455   *            ASYNC - Will configure the event and return immediately.
00456   *
00457   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00458   *
00459   *            SYNC_SLEEP - Will configure the event and block the current fiber until the
00460   *                         event is received.
00461   *
00462   * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
00463   *
00464   * @note delimeters are matched on a per byte basis.
00465   */
00466 int MicroBitUARTService::eventOn(ManagedString delimeters, MicroBitSerialMode mode)
00467 {
00468     if(mode == SYNC_SPINWAIT)
00469         return MICROBIT_INVALID_PARAMETER;
00470 
00471     //configure our head match...
00472     this->delimeters = delimeters;
00473 
00474     //block!
00475     if(mode == SYNC_SLEEP)
00476         fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH);
00477 
00478     return MICROBIT_OK;
00479 }
00480 
00481 /**
00482   * Configures an event to be fired after "len" characters.
00483   *
00484   * @param len the number of characters to wait before triggering the event
00485   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00486   *        gives a different behaviour:
00487   *
00488   *            ASYNC - Will configure the event and return immediately.
00489   *
00490   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00491   *
00492   *            SYNC_SLEEP - Will configure the event and block the current fiber until the
00493   *                         event is received.
00494   *
00495   * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
00496   */
00497 int MicroBitUARTService::eventAfter(int len, MicroBitSerialMode mode)
00498 {
00499     if(mode == SYNC_SPINWAIT)
00500         return MICROBIT_INVALID_PARAMETER;
00501 
00502     //configure our head match...
00503     this->rxBuffHeadMatch = (rxBufferHead + len) % rxBufferSize;
00504 
00505     //block!
00506     if(mode == SYNC_SLEEP)
00507         fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH);
00508 
00509     return MICROBIT_OK;
00510 }
00511 
00512 /**
00513   * Determines if we have space in our rxBuff.
00514   *
00515   * @return 1 if we have space, 0 if we do not.
00516   *
00517   * @note the reason we do not wrap the super's readable() method is so that we
00518   *       don't interfere with communities that use manual calls to uBit.serial.readable()
00519   */
00520 int MicroBitUARTService::isReadable()
00521 {
00522     return (rxBufferTail != rxBufferHead) ? 1 : 0;
00523 }
00524 
00525 /**
00526   * @return The currently buffered number of bytes in our rxBuff.
00527   */
00528 int MicroBitUARTService::rxBufferedSize ()
00529 {
00530     if(rxBufferTail > rxBufferHead)
00531         return (rxBufferSize - rxBufferTail) + rxBufferHead;
00532 
00533     return rxBufferHead - rxBufferTail;
00534 }
00535 
00536 /**
00537   * @return The currently buffered number of bytes in our txBuff.
00538   */
00539 int MicroBitUARTService::txBufferedSize ()
00540 {
00541     if(txBufferTail > txBufferHead)
00542         return (txBufferSize - txBufferTail) + txBufferHead;
00543 
00544     return txBufferHead - txBufferTail;
00545 }