mbed.org local branch of microbit-dal. The real version lives in git at https://github.com/lancaster-university/microbit-dal

Dependencies:   BLE_API nRF51822 mbed-dev-bin

Dependents:   microbit Microbit IoTChallenge1 microbit ... more

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* txCharacteristic = NULL;
00044 
00045 /**
00046   * A callback function for whenever a Bluetooth device consumes our TX Buffer
00047   */
00048 void on_confirmation(uint16_t handle)
00049 {
00050     if(handle == txCharacteristic->getValueAttribute().getHandle())
00051     {
00052         txBufferTail = txBufferHead;
00053         MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY);
00054     }
00055 }
00056 
00057 /**
00058  * Constructor for the UARTService.
00059  * @param _ble an instance of BLEDevice
00060  * @param rxBufferSize the size of the rxBuffer
00061  * @param txBufferSize the size of the txBuffer
00062  *
00063  * @note defaults to 20
00064  */
00065 MicroBitUARTService::MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize, uint8_t txBufferSize) : ble(_ble)
00066 {
00067     rxBufferSize += 1;
00068     txBufferSize += 1;
00069 
00070     txBuffer = (uint8_t *)malloc(txBufferSize);
00071     rxBuffer = (uint8_t *)malloc(rxBufferSize);
00072 
00073     rxBufferHead = 0;
00074     rxBufferTail = 0;
00075     this->rxBufferSize = rxBufferSize;
00076 
00077     txBufferHead = 0;
00078     txBufferTail = 0;
00079     this->txBufferSize = txBufferSize;
00080 
00081     GattCharacteristic rxCharacteristic(UARTServiceRXCharacteristicUUID, rxBuffer, 1, rxBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
00082 
00083     txCharacteristic = new GattCharacteristic(UARTServiceTXCharacteristicUUID, txBuffer, 1, txBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);
00084 
00085     GattCharacteristic *charTable[] = {txCharacteristic, &rxCharacteristic};
00086 
00087     GattService uartService(UARTServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
00088 
00089     _ble.addService(uartService);
00090 
00091     this->rxCharacteristicHandle = rxCharacteristic.getValueAttribute().getHandle();
00092 
00093     _ble.gattServer().onDataWritten(this, &MicroBitUARTService::onDataWritten);
00094     _ble.gattServer().onConfirmationReceived(on_confirmation);
00095 }
00096 
00097 /**
00098   * A callback function for whenever a Bluetooth device writes to our RX characteristic.
00099   */
00100 void MicroBitUARTService::onDataWritten(const GattWriteCallbackParams *params) {
00101     if (params->handle == this->rxCharacteristicHandle)
00102     {
00103         uint16_t bytesWritten = params->len;
00104 
00105         for(int byteIterator = 0; byteIterator <  bytesWritten; byteIterator++)
00106         {
00107             int newHead = (rxBufferHead + 1) % rxBufferSize;
00108 
00109             if(newHead != rxBufferTail)
00110             {
00111                 char c = params->data[byteIterator];
00112 
00113                 int delimeterOffset = 0;
00114                 int delimLength = this->delimeters.length();
00115 
00116                 //iterate through our delimeters (if any) to see if there is a match
00117                 while(delimeterOffset < delimLength)
00118                 {
00119                     //fire an event if there is to block any waiting fibers
00120                     if(this->delimeters.charAt(delimeterOffset) == c)
00121                         MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH);
00122 
00123                     delimeterOffset++;
00124                 }
00125 
00126                 rxBuffer[rxBufferHead] = c;
00127 
00128                 rxBufferHead = newHead;
00129 
00130                 if(rxBufferHead == rxBuffHeadMatch)
00131                 {
00132                     rxBuffHeadMatch = -1;
00133                     MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH);
00134                 }
00135             }
00136             else
00137                 MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_RX_FULL);
00138         }
00139     }
00140 }
00141 
00142 /**
00143   * An internal method that copies values from a circular buffer to a linear buffer.
00144   *
00145   * @param circularBuff a pointer to the source circular buffer
00146   * @param circularBuffSize the size of the circular buffer
00147   * @param linearBuff a pointer to the destination linear buffer
00148   * @param tailPosition the tail position in the circular buffer you want to copy from
00149   * @param headPosition the head position in the circular buffer you want to copy to
00150   *
00151   * @note this method assumes that the linear buffer has the appropriate amount of
00152   *       memory to contain the copy operation
00153   */
00154 void MicroBitUARTService::circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition)
00155 {
00156     int toBuffIndex = 0;
00157 
00158     while(tailPosition != headPosition)
00159     {
00160         linearBuff[toBuffIndex++] = circularBuff[tailPosition];
00161 
00162         tailPosition = (tailPosition + 1) % circularBuffSize;
00163     }
00164 }
00165 
00166 /**
00167   * Retreives a single character from our RxBuffer.
00168   *
00169   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00170   *        gives a different behaviour:
00171   *
00172   *            ASYNC - Will attempt to read a single character, and return immediately
00173   *
00174   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00175   *
00176   *            SYNC_SLEEP - Will configure the event and block the current fiber until the
00177   *                         event is received.
00178   *
00179   * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, a character or MICROBIT_NO_DATA
00180   */
00181 int MicroBitUARTService::getc(MicroBitSerialMode mode)
00182 {
00183     if(mode == SYNC_SPINWAIT)
00184         return MICROBIT_INVALID_PARAMETER;
00185 
00186     if(mode == ASYNC)
00187     {
00188         if(!isReadable())
00189             return MICROBIT_NO_DATA;
00190     }
00191 
00192     if(mode == SYNC_SLEEP)
00193     {
00194         if(!isReadable())
00195             eventAfter(1, mode);
00196     }
00197 
00198     char c = rxBuffer[rxBufferTail];
00199 
00200     rxBufferTail = (rxBufferTail + 1) % rxBufferSize;
00201 
00202     return c;
00203 }
00204 
00205 /**
00206   * places a single character into our transmission buffer,
00207   *
00208   * @param c the character to transmit
00209   *
00210   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00211   *        gives a different behaviour:
00212   *
00213   *            ASYNC - Will copy as many characters as it can into the buffer for transmission,
00214   *                    and return control to the user.
00215   *
00216   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00217   *
00218   *            SYNC_SLEEP - Will perform a cooperative blocking wait until all
00219   *                         given characters have been received by the connected
00220   *                         device.
00221   *
00222   * @return the number of characters written, or MICROBIT_NOT_SUPPORTED if there is
00223   *         no connected device, or the connected device has not enabled indications.
00224   */
00225 int MicroBitUARTService::putc(char c, MicroBitSerialMode mode)
00226 {
00227     return (send((uint8_t *)&c, 1, mode) == 1) ? 1 : EOF;
00228 }
00229 
00230 /**
00231   * Copies characters into the buffer used for Transmitting to the central device.
00232   *
00233   * @param buf a buffer containing length number of bytes.
00234   * @param length the size of the buffer.
00235   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00236   *        gives a different behaviour:
00237   *
00238   *            ASYNC - Will copy as many characters as it can into the buffer for transmission,
00239   *                    and return control to the user.
00240   *
00241   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00242   *
00243   *            SYNC_SLEEP - Will perform a cooperative blocking wait until all
00244   *                         given characters have been received by the connected
00245   *                         device.
00246   *
00247   * @return the number of characters written, or MICROBIT_NOT_SUPPORTED if there is
00248   *         no connected device, or the connected device has not enabled indications.
00249   */
00250 int MicroBitUARTService::send(const uint8_t *buf, int length, MicroBitSerialMode mode)
00251 {
00252     if(length < 1 || mode == SYNC_SPINWAIT)
00253         return MICROBIT_INVALID_PARAMETER;
00254 
00255     bool updatesEnabled = false;
00256 
00257     ble.gattServer().areUpdatesEnabled(*txCharacteristic, &updatesEnabled);
00258 
00259     if(!ble.getGapState().connected && !updatesEnabled)
00260         return MICROBIT_NOT_SUPPORTED;
00261 
00262     int bytesWritten = 0;
00263 
00264     while(bytesWritten < length && ble.getGapState().connected && updatesEnabled)
00265     {
00266         for(int bufferIterator = bytesWritten; bufferIterator < length; bufferIterator++)
00267         {
00268             int nextHead = (txBufferHead + 1) % txBufferSize;
00269 
00270             if(nextHead != txBufferTail)
00271             {
00272                 txBuffer[txBufferHead] = buf[bufferIterator];
00273 
00274                 txBufferHead = nextHead;
00275 
00276                 bytesWritten++;
00277             }
00278         }
00279 
00280         int size = txBufferedSize ();
00281 
00282         uint8_t temp[size];
00283 
00284         memclr(&temp, size);
00285 
00286         circularCopy(txBuffer, txBufferSize, temp, txBufferTail, txBufferHead);
00287 
00288 
00289         if(mode == SYNC_SLEEP)
00290             fiber_wake_on_event(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY);
00291 
00292         ble.gattServer().write(txCharacteristic->getValueAttribute().getHandle(), temp, size);
00293 
00294         if(mode == SYNC_SLEEP)
00295             schedule();
00296         else
00297             break;
00298 
00299         ble.gattServer().areUpdatesEnabled(*txCharacteristic, &updatesEnabled);
00300     }
00301 
00302     return bytesWritten;
00303 }
00304 
00305 /**
00306   * Copies characters into the buffer used for Transmitting to the central device.
00307   *
00308   * @param s the string to transmit
00309   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00310   *        gives a different behaviour:
00311   *
00312   *            ASYNC - Will copy as many characters as it can into the buffer for transmission,
00313   *                    and return control to the user.
00314   *
00315   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00316   *
00317   *            SYNC_SLEEP - Will perform a cooperative blocking wait until all
00318   *                         given characters have been received by the connected
00319   *                         device.
00320   *
00321   * @return the number of characters written, or MICROBIT_NOT_SUPPORTED if there is
00322   *         no connected device, or the connected device has not enabled indications.
00323   */
00324 int MicroBitUARTService::send(ManagedString s, MicroBitSerialMode mode)
00325 {
00326     return send((uint8_t *)s.toCharArray(), s.length(), mode);
00327 }
00328 
00329 /**
00330   * Reads a number of characters from the rxBuffer and fills user given buffer.
00331   *
00332   * @param buf a pointer to a buffer of len bytes.
00333   * @param len the size of the user allocated buffer
00334   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00335   *        gives a different behaviour:
00336   *
00337   *            ASYNC - Will attempt to read all available characters, and return immediately
00338   *                    until the buffer limit is reached
00339   *
00340   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00341   *
00342   *            SYNC_SLEEP - Will first of all determine whether the given number of characters
00343   *                         are available in our buffer, if not, it will set an event and sleep
00344   *                         until the number of characters are avaialable.
00345   *
00346   * @return the number of characters digested
00347   */
00348 int MicroBitUARTService::read(uint8_t *buf, int len, MicroBitSerialMode mode)
00349 {
00350     if(mode == SYNC_SPINWAIT)
00351         return MICROBIT_INVALID_PARAMETER;
00352 
00353     int i = 0;
00354 
00355     if(mode == ASYNC)
00356     {
00357         int c;
00358 
00359         while((c = getc(mode)) > 0 && i < len)
00360         {
00361             buf[i] = c;
00362             i++;
00363         }
00364     }
00365 
00366     if(mode == SYNC_SLEEP)
00367     {
00368         if(len > rxBufferedSize ())
00369             eventAfter(len - rxBufferedSize (), mode);
00370 
00371         while(i < len)
00372         {
00373             buf[i] = (char)getc(mode);
00374             i++;
00375         }
00376     }
00377 
00378     return i;
00379 }
00380 
00381 /**
00382   * Reads a number of characters from the rxBuffer and returns them as a ManagedString
00383   *
00384   * @param len the number of characters to read.
00385   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00386   *        gives a different behaviour:
00387   *
00388   *            ASYNC - Will attempt to read all available characters, and return immediately
00389   *                    until the buffer limit is reached
00390   *
00391   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00392   *
00393   *            SYNC_SLEEP - Will first of all determine whether the given number of characters
00394   *                         are available in our buffer, if not, it will set an event and sleep
00395   *                         until the number of characters are avaialable.
00396   *
00397   * @return an empty ManagedString on error, or a ManagedString containing characters
00398   */
00399 ManagedString MicroBitUARTService::read(int len, MicroBitSerialMode mode)
00400 {
00401     uint8_t buf[len + 1];
00402 
00403     memclr(&buf, len + 1);
00404 
00405     int ret = read(buf, len, mode);
00406 
00407     if(ret < 1)
00408         return ManagedString();
00409 
00410     return ManagedString((const char *)buf);
00411 }
00412 
00413 /**
00414   * Reads characters until a character matches one of the given delimeters
00415   *
00416   * @param delimeters the number of characters to match against
00417   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00418   *        gives a different behaviour:
00419   *
00420   *            ASYNC - Will attempt read the immediate buffer, and look for a match.
00421   *                    If there isn't, an empty ManagedString will be returned.
00422   *
00423   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00424   *
00425   *            SYNC_SLEEP - Will first of all consider the characters in the immediate buffer,
00426   *                         if a match is not found, it will block on an event, fired when a
00427   *                         character is matched.
00428   *
00429   * @return an empty ManagedString on error, or a ManagedString containing characters
00430   */
00431 ManagedString MicroBitUARTService::readUntil(ManagedString delimeters, MicroBitSerialMode mode)
00432 {
00433     if(mode == SYNC_SPINWAIT)
00434         return MICROBIT_INVALID_PARAMETER;
00435 
00436     int localTail = rxBufferTail;
00437     int preservedTail = rxBufferTail;
00438 
00439     int foundIndex = -1;
00440 
00441     //ASYNC mode just iterates through our stored characters checking for any matches.
00442     while(localTail != rxBufferHead && foundIndex  == -1)
00443     {
00444         //we use localTail to prevent modification of the actual tail.
00445         char c = rxBuffer[localTail];
00446 
00447         for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++)
00448             if(delimeters.charAt(delimeterIterator) == c)
00449                 foundIndex = localTail;
00450 
00451         localTail = (localTail + 1) % rxBufferSize;
00452     }
00453 
00454     //if our mode is SYNC_SLEEP, we set up an event to be fired when we see a
00455     //matching character.
00456     if(mode == SYNC_SLEEP && foundIndex == -1)
00457     {
00458         eventOn(delimeters, mode);
00459 
00460         foundIndex = rxBufferHead - 1;
00461 
00462         this->delimeters = ManagedString();
00463     }
00464 
00465     if(foundIndex >= 0)
00466     {
00467         //calculate our local buffer size
00468         int localBuffSize = (preservedTail > foundIndex) ? (rxBufferSize - preservedTail) + foundIndex : foundIndex - preservedTail;
00469 
00470         uint8_t localBuff[localBuffSize + 1];
00471 
00472         memclr(&localBuff, localBuffSize + 1);
00473 
00474         circularCopy(rxBuffer, rxBufferSize, localBuff, preservedTail, foundIndex);
00475 
00476         //plus one for the character we listened for...
00477         rxBufferTail = (rxBufferTail + localBuffSize + 1) % rxBufferSize;
00478 
00479         return ManagedString((char *)localBuff, localBuffSize);
00480     }
00481 
00482     return ManagedString();
00483 }
00484 
00485 /**
00486   * Configures an event to be fired on a match with one of the delimeters.
00487   *
00488   * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n")
00489   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00490   *        gives a different behaviour:
00491   *
00492   *            ASYNC - Will configure the event and return immediately.
00493   *
00494   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00495   *
00496   *            SYNC_SLEEP - Will configure the event and block the current fiber until the
00497   *                         event is received.
00498   *
00499   * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
00500   *
00501   * @note delimeters are matched on a per byte basis.
00502   */
00503 int MicroBitUARTService::eventOn(ManagedString delimeters, MicroBitSerialMode mode)
00504 {
00505     if(mode == SYNC_SPINWAIT)
00506         return MICROBIT_INVALID_PARAMETER;
00507 
00508     //configure our head match...
00509     this->delimeters = delimeters;
00510 
00511     //block!
00512     if(mode == SYNC_SLEEP)
00513         fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH);
00514 
00515     return MICROBIT_OK;
00516 }
00517 
00518 /**
00519   * Configures an event to be fired after "len" characters.
00520   *
00521   * @param len the number of characters to wait before triggering the event
00522   * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
00523   *        gives a different behaviour:
00524   *
00525   *            ASYNC - Will configure the event and return immediately.
00526   *
00527   *            SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
00528   *
00529   *            SYNC_SLEEP - Will configure the event and block the current fiber until the
00530   *                         event is received.
00531   *
00532   * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
00533   */
00534 int MicroBitUARTService::eventAfter(int len, MicroBitSerialMode mode)
00535 {
00536     if(mode == SYNC_SPINWAIT)
00537         return MICROBIT_INVALID_PARAMETER;
00538 
00539     //configure our head match...
00540     this->rxBuffHeadMatch = (rxBufferHead + len) % rxBufferSize;
00541 
00542     //block!
00543     if(mode == SYNC_SLEEP)
00544         fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH);
00545 
00546     return MICROBIT_OK;
00547 }
00548 
00549 /**
00550   * Determines if we have space in our rxBuff.
00551   *
00552   * @return 1 if we have space, 0 if we do not.
00553   *
00554   * @note the reason we do not wrap the super's readable() method is so that we
00555   *       don't interfere with communities that use manual calls to uBit.serial.readable()
00556   */
00557 int MicroBitUARTService::isReadable()
00558 {
00559     return (rxBufferTail != rxBufferHead) ? 1 : 0;
00560 }
00561 
00562 /**
00563   * @return The currently buffered number of bytes in our rxBuff.
00564   */
00565 int MicroBitUARTService::rxBufferedSize ()
00566 {
00567     if(rxBufferTail > rxBufferHead)
00568         return (rxBufferSize - rxBufferTail) + rxBufferHead;
00569 
00570     return rxBufferHead - rxBufferTail;
00571 }
00572 
00573 /**
00574   * @return The currently buffered number of bytes in our txBuff.
00575   */
00576 int MicroBitUARTService::txBufferedSize ()
00577 {
00578     if(txBufferTail > txBufferHead)
00579         return (txBufferSize - txBufferTail) + txBufferHead;
00580 
00581     return txBufferHead - txBufferTail;
00582 }